20120116

ACM 497 Strategic Defense Initiative

一樣是Longest Increasing Subsequence……
結果用ACM481的方法去解竟然WA,囧rz。
用正常O(n ^ 2)解就AC……大概是我方法哪裡不對吧,
然後ACM481剛剛好AC……無解!

/* ACM 497 Strategic Defense Initiative
 * mythnc
 * 2012/01/16 20:42:20   
 * run time: 0.016
 */
#include <stdio.h>

#define MAXN    100000
#define LINEMAX 50

void input(void);
void lis(void);

int n;
int seq[MAXN], len[MAXN], pre[MAXN];

int main(void)
{
    int set, i;

    scanf("%d", &set);
    getchar();
    getchar();
    for (i = 0; i < set; i++) {
        input();
        if (i > 0)
            putchar('\n');
        lis();
    }

    return 0;
}

/* input: receive input data */
void input(void)
{
    char line[LINEMAX];

    n = 0;
    while (fgets(line, LINEMAX, stdin) && line[0] != '\n')
        sscanf(line, "%d", &seq[n++]);
}

/* lis: find lis and output */
void lis(void)
{
    int i, j, maxi;
    int out[MAXN];
    /* init */
    for (i = 0; i < n; i++) {
        len[i] = 1;
        pre[i] = -1;
    }

    for (i = 0; i < n; i++)
        for (j = i + 1; j < n; j++)
            if (seq[j] > seq[i] && len[j] < len[i] + 1) {
                len[j] = len[i] + 1;
                pre[j] = i;
            }

    for (i = 1, maxi = 0; i < n; i++)
        if (len[i] > len[maxi])
            maxi = i;

    /* output */
    printf("Max hits: %d\n", len[maxi]);
    i = 0;
    while (1) {
        out[i++] = seq[maxi];
        if (pre[maxi] == -1)
            break;
        maxi = pre[maxi];
    }
    for (j = i - 1; j > -1; j--)
        printf("%d\n", out[j]);
}

ACM 481 What Goes Up

Longest Increasing Subsequence問題。
用正常的O(n ^ 2)的演算法去解竟然會TL,傻眼……
結果只好開始看別人是怎麼AC的。
結果AC一定要用O(nlogn)的演算法……
就開始看……我發現我看不懂 ToT。

反正就是找了一堆資料,然後寫法都很數學式,又是英文,
而且又沒範例 = =,只能說GG啊。

最後還是在這裡
找到範例碼,真是揪甘心~

重點就是要了解Patience sorting的運作原理即可。
Patience sorting大概是這樣:
1.如果沒有pile,就做一個pile。
2.每次把x值與pile最上的值相比,若x值 < pile的top值,
就把x放到該pile上。
若無,就再做一個pile。
3.反覆,到沒有x值為此。

這樣講可能很複雜,直接舉實例說明:
-7 10 9 2 3 8 8 1。
一開始是-7,沒pile,所以給-7一個pile:
-7,
接著是10,10 > -7,所以make a new pile:
-7
10,
再來是9, 9 > -7,所以不能放在pile1,
但9 < 10,所以放在pile2上,
-7
10 9,
反覆此動作我們最後可以得到:
-7
10 9 2 1
3
8
(因為Patience sorting是針對撲克牌做sort,
所以有重複的數字,我們就不再處理第二次)
如此,我們得到4個pile。
也就是lis為4。
第1到第4個位置分別可以放-7、10 9 2 1、3、8。
注意,上面這個例子並不表示,所有lis為4的組合……
也就是每個數字其實是獨立的,不能放在一起看,
除非我們找到一組順序,使得此子序列的對應關係合於本來的序列。
好吧,其實我不是很懂Patience sorting……
總之pile數 = lis數就對了!
那這樣要怎麼找到一組lis呢?
現在我們已經知道,每個位置能放的值了,
所以按照題目要求,我們只要從最末的值開始往前找即可。
這部份就跟O(n ^ 2)的方法是一樣的。

接著談實作,實作非常之令我苦惱……
所謂懂還不一定寫得出程式碼,就是這回事,囧。
首先要做出一個pile,所以讓seq[0]為第一個pile,
然後我們可以發現,Patience sorting其實是一個遞增數列(這應該是重點)。
也就是說,每個pile最上面的值,排在一起,一定為遞增數列。
既然是遞增,那我們可以用binary search去做search的動作。
(因為已經sort,所以可以用bin search)
兩種情況:
(1)seq[x]的值,比最後一個pile 的top值還大,就增加一個pile。
因為我們已知所有pile的top為一遞增數列,
那如果seq[x] > 最後一個pile的top值,
勢必seq[x],大於所有pile的tope值,
所以這時候就要增加一個pile。
(2)非(1)的情況,表示seq[x] <= 最後一個pile的top值。
這時候,seq[x]這個值,可能跟前面某個pile的top值一樣,
或是介於某兩個pile值之間的情況。
如果是前者,那我們找到seq[x] == pile top時,任務就結束了。
如果是後者,那我們會得到pile[i] top < seq[x] < pile[i + 1] top。
這時,需把pile[i + 1]的top換成seq[x]才行。
如此才符合Patience sorting的第二個定義。

所以我們可以得到一種資料結構:stack包含stack,
但是實作上不用這麼麻煩,只要用stack + 取代就好了。
其實更好的作法是再用一個空間,去紀錄每個seq[x]的在lis位置,
就是紀錄他在哪個pile啦!用空間換取時間!

這樣,我們已經知道了pile數(lis長度),
也紀錄了每個seq[x]所在lis長度的位置,
那麼這題就有解了!
從最後一個seq[x]往前到seq[0],去尋找符合lis長度的值,
找到該值後記錄下來,再把lis長度 - 1,同樣做找值的動作,
一直找到第一個lis為止。
如此,就是答案了。

另外為什麼是O(n * log(n))呢,
因為binary search是O(log(n)),
而有n個值,最慘的情況就是每個值都去做binary search,
那就會得到O(n * log(n))。

再一次感謝DJWS~
有興趣可以再看一些參考資料
wiki跟algorithmist都有。

/* ACM 481 What Goes Up
 * mythnc
 * 2012/01/16 09:58:36   
 * run time: 0.044
 */
#include <stdio.h>

#define MAXN 100000

void lis(int);
void binsearch(int, int, int);

int n;
int seq[MAXN], pos[MAXN], v[MAXN];

int main(void)
{

    n = 0;
    while (scanf("%d", &seq[n]) == 1)
        n++;

    lis(n);

    return 0;
}

/* lis: return the last max len position */
void lis(int n)
{
    int len, i, j;
    int out[MAXN];

    len = 0;
    v[len++] = seq[0];
    pos[0] = 0;

    for (i = 1; i < n; i++) {
        if (seq[i] > v[len - 1]) {
            v[len] = seq[i];
            pos[i] = len++;
        }
        else
            binsearch(0, len, i);
    }
    /* output */
    printf("%d\n-\n", len);
    for (i = n - 1, j = len - 1; i > -1 && j > -1; i--)
        if (pos[i] == j) {
            out[j] = seq[i];
            j--;
        }
    for (i = 0; i < len; i++)
        printf("%d\n", out[i]);
}

void binsearch(int begin, int end, int index)
{
    int mid;

    while (begin <= end) {
        mid = (begin + end) / 2;
        if (v[mid] == seq[index]) {
            pos[index] = mid;
            return;
        }
        else if (v[mid] > seq[index])
            end = mid - 1;
        else
            begin = mid + 1;
    }
    
    mid = (begin + end) / 2;
    if (mid == 0 && index == 1) {
        v[0] = seq[index];
        pos[index] = 0;
    }
    else {
        v[mid + 1] = seq[index];
        pos[index] = mid + 1;
    }
}

20120115

ACM 231 Testing the CATCHER

Longest Increasing Subsequence反過來做!
變成Longest Decreasing Subsequence!

/* ACM 231 Testing the CATCHER
 * mythnc
 * 2012/01/15 21:19:58   
 * run time: 0.004
 */
#include <stdio.h>

int input(int *);
int lds(int *, int);

#define MAXM 10000

int main(void)
{
    int set, data;
    int catcher[MAXM];

    set = 0;
    while (scanf("%d", &data) && data != -1) {
        catcher[0] = data;
        if (set > 0)
            putchar('\n');
        printf("Test #%d:\n", ++set);
        printf("  maximum possible interceptions: %d\n",
               lds(catcher, input(catcher)));
    }

    return 0;
}

/* input: receive input data
 * and return the number of missile */
int input(int *catcher)
{
    int i;

    i = 1;
    while (scanf("%d", &catcher[i]) && catcher[i] != -1)
        i++;

    return i;
}

/* lds: return the longest decrement subsequence length */
int lds(int *catcher, int n)
{
    int len[MAXM];
    int i, j, maxlen;

    for (i = 0; i < n; i++)
        len[i] = 1;

    for (i = 0; i < n; i++)
        for (j = i + 1; j < n; j++)
            if (catcher[j] < catcher[i] && len[j] < len[i] + 1)
                len[j] = len[i] + 1;
                
    for (i = maxlen = 0; i < n; i++)
        if (len[i] > maxlen)
            maxlen = len[i];

    return maxlen;
}

ACM 103 Stacking Boxes

其實是一個Longest Increasing Subsequence……
相關的教學可以看這裡
之前想說應該是做3個sort,
先對維度做sort,接著對每個維度的第一個維度sort,
接著把所有的box依照sort就有解。
後來想想好像不對……

所以第三步驟其實不是作sort,是做LIS。
把整個題目想成是一個box lis就很簡單了。
單一個box由n個維度構成,所以box的大小由n個維度所決定。
而維度內先排列是必要的(因為要跟其他box比大小)
而題目要作最多的「大包小」的動作。
所以先把每個box依照大小順序做排列也是必要的。
如此小的在前面,大的在後面,才可以找出最多的「大包小」。

/* ACM 103 Stacking Boxes
 * mythnc
 * 2012/01/15 14:42:45   
 * run time: 0.008
 */
#include <stdio.h>
#include <stdlib.h>

#define MAXBOX 30
#define MAXD   10

typedef struct box {
    int num;
    int dimen[MAXD];
} Box;

void init(int, int);
int cmpd(const void *, const void *);
int cmpb(const void *, const void *);
int lis(int, int);
void output(int);

Box b[MAXBOX];
int len[MAXBOX], pre[MAXBOX];

int main(void)
{
    int n, d;

    while (scanf("%d %d", &n, &d) == 2) {
        init(n, d);
        output(lis(n, d));
    }

    return 0;
}

/* init: receive input data, initialize b content,
 * and sort data */
void init(int n, int d)
{
    int i, j;

    for (i = 0; i < n; i++) {
        b[i].num = i + 1;
        len[i] = 1;
        pre[i] = -1;
        for (j = 0; j < d; j++)
            scanf("%d", &b[i].dimen[j]);
        qsort(b[i].dimen, d, sizeof(int), cmpd);
    }
    qsort(b, n, sizeof(Box), cmpb);
}

/* cmpd: sort dimension for qsort() */
int cmpd(const void *a, const void *b)
{
    return *(int *)a - *(int *)b;
}

/* cmpb: sort dimen[0] for qsort() */
int cmpb(const void *a, const void *b)
{
    return ((Box *)a)->dimen[0] - ((Box *)b)->dimen[0];
}

/* lis: find longest increasing subsequence.
 * the unit is "box"
 * return its pos */
int lis(int n, int d)
{
    int i, j, k, maxi;

    for (i = 0; i < n; i++)
        for (j = i + 1; j < n; j++) {
            for (k = 0; k < d && b[j].dimen[k] > b[i].dimen[k]; k++)
                ;
            if (k == d && len[i] + 1 > len[j]) {
                len[j] = len[i] + 1;
                pre[j] = i;
            }
        }

    for (i = 1, maxi = 0; i < n; i++)
        if (len[i] > len[maxi])
            maxi = i;

    return maxi;
}

/* output: output lis len and seq */
void output(int i)
{
    int num;
    int inc[MAXBOX];
    /* output max lis len */
    printf("%d\n", len[i]);

    num = 0;
    while (1) {
        inc[num++] = b[i].num;
        if (pre[i] == -1)
            break;
        i = pre[i];
    }
    /* out lis seq */
    printf("%d", inc[--num]);
    for (i = num - 1; i > -1; i--)
        printf(" %d", inc[i]);
    putchar('\n');
}

20120114

ACM 10608 Friends

Union-Find Disjoint Sets。
ACM10583
這次用weightedunion + collapsingfind去解。

原本直接把第一行吃掉不處理,
結果一直WA……
後來吃掉第一行並做處理後就AC了 =.=。
這測資有問題……可能是m後面不只m行……
心機啊!

/* ACM 10608 Friends
 * mythnc
 * 2012/01/14 23:12:34   
 * run time: 0.076
 */
#include <stdio.h>

#define MAXNODE 30001

void init(int);
void input(int);
int collapsingfind(int);
void weightedunion(int, int);
int count(int);

int node[MAXNODE];

int main(void)
{
    int n, m, set;

    scanf("%d", &set);
    while(set--) {
        scanf("%d %d", &n, &m);
        init(n);
        input(m);
        printf("%d\n", count(n));
    }

    return 0;
}

/* init: initialize each node to different sets */
void init(int n)
{
    int i;

    for (i = 1; i <= n; i++)
        node[i] = -1;
}

void input(int n)
{
    int i, v1, v2;

    for (i = 0; i < n; i++) {
        scanf("%d %d", &v1, &v2);
        weightedunion(collapsingfind(v1), collapsingfind(v2));
    }
}

/* collapsingfind: find root first,
 * then use collapsing rule to collapse all nodes
 * form v to root. return the set of node v */
int collapsingfind(int v)
{
    int root, trail, tmp;

    for (root = v; node[root] >= 0; root = node[root])
        ;
    for (trail = v; trail != root; trail = node[trail]) {
        tmp = trail;
        node[tmp] = root;
    }

    return root;
}

/* weightedunoin: union s1 and s2 */
void weightedunion(int s1, int s2)
{
    if (s1 == s2)
        return;

    if (s1 <= s2) {
        node[s1] += node[s2];
        node[s2] = s1;
    }
    else {
        node[s2] += node[s1];
        node[s1] = s2;
    }
}

/* count: return the max nodes set */
int count(int n)
{
    int min, i;

    for (i = 1, min = -1; i <= n; i++)
        if (node[i] < min)
            min = node[i];

    return -min;
}