A Za, A Za, Fighting...

坚信:勤能补拙

在磕磕绊绊中,终于做完一百题,完成暑期目标,会继续加油...

附: summary (revision 37)

Summary of PKU solved, since 05-20-2010

------------------------Fighting-----------------------------
------------------------Fighting-----------------------------

[sort]
1002 487-3279
1007 DNA Sorting
1468 Rectangles
2299 Ultra-QuickSort
2388 Who's in the Middle

[search(dfs, bfs, backtracking)]
1020 Anniversary Cake [good]
1077 Eight [classic][bfs, bbfs, A*]
1101 The Game
1111 Image Perimeters [interesting]
1129 Channel Allocation [classic]
1164 The Castle
1166 The Clocks
1190 Birthday Cake [good]
1198 Solitaire [classic]
1321 Chessboard Problem
1416 Shredding Company
1426 Find The Multiple
1475 Pushing Boxes [complex]
1562 Oil Deposits
1606 Jugs
1629 Fillword
1691 Painting A Board
1724 ROADS
1753 Flip Game
1775 Sum of Factorials
1915 Knight Moves
1950 Dessert
2248 Addition Chains
2251 Dungeon Master
2488 A Knight's Journey
2531 Network Saboteur
2676 Sudoku
3009 Curling 2.0
3083 Children of the Candy Corn
3087 Shuffle'm Up
3126 Prime Path
3256 Cow Picnic
3278 Catch That Cow
3373 Changing Digits [hard]
3411 Paid Roads
3414 Pots

[dynamic programming/memory function]
1014 Dividing [good & hard]
1015 Jury Compromise
1050 To the Max [classic]
1080 Human Gene Functions
1088 Skiing [good]
1157 Little shop of flowers
1159 Palindrome
1160 Post Office [good]
1163 The Triangle
1179 Polygon [classic]
1185 Artillery position [classic, scdp]
1260 Pearls [good]
1276 Cash Machine [classic]
1458 Common Subsequence [classic]
1579 Function Run Fun
1631 Bridging signals
1887 Testing the CATCHER/2533 Longest Ordered Subsequence [classic]
1953 World Cup Noise
2192 Zipper
2250 Compromise
2479 Maximum sum/2593 Max Sequence
3356 AGTC
3624 Charm Bracelet

[greedy algorithm]

[data structure]
1028 Web Navigation (stack/array)
1611 The Suspects (union-find set)
1988 Cube Stacking (union-find set)
2524 Ubiquitous Religions (union-find set)
3253 Fence Repair (huffman tree)
3349 Snowflake Snow Snowflakes (Hash & Minimal Representation)

[graph algorithm]

[others]
1001 Exponentiation (high precision)
1008 Maya Calendar
1083 Moving Tables (novel perspective) [good]
1176 Party Lamps (enumerate)
1226 Substrings
1731 Orders (permutation) [good]
2080 Calendar
2244 Eeny Meeny Moo (Josephus Problem) [good]
2503 Babelfish (string hash)
2663 Tri Tiling (recurrence) [good]

[easy/water]
1000 A+B Problem
1003 Hangover
1004 Financial Management
1005 I Think I Need a Houseboat
1298 The Hardest Problem Ever
1316 Self Numbers
1477 Box of Bricks
1543 Perfect Cubes (build table)
1547 Clay Bully
1552 Doubles
1565 Skew Binary
1595 Prime Cuts
1936 All in All [good]
2081 Recaman's Sequence
2105 IP Address
2726 Holiday Hotel [funny]

posted @ 2010-08-19 14:47 simplyzhao 阅读(143) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1111

思路:
其实,这是一道简单题,关键是要看透如何求周长: 连通块每格四个方向(上下左右)如果不在连通块内(超出范围或者Empty)则周长加一
DFS或者BFS都可以解决

代码:
 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #define MAX_LEN 21
 5 #define is_valid(x,y) (x>=0 && x<row && y>=0 && y<col)
 6 const int dx[] = {-1100-1-111};
 7 const int dy[] = {00-11-11-11};
 8 struct Point {
 9     int x, y;
10 }queue[MAX_LEN*MAX_LEN];
11 char grid[MAX_LEN][MAX_LEN];
12 int visited[MAX_LEN][MAX_LEN];
13 int row, col, start_x, start_y;
14 int rt;
15 
16 void
17 calculate(int x, int y)
18 {
19     int i, tmpx, tmpy;
20     for(i=0; i<4; i++) {
21         tmpx = x + dx[i];
22         tmpy = y + dy[i];
23         if(!is_valid(tmpx, tmpy) || grid[tmpx][tmpy]=='.')
24             ++rt;
25     }
26 }
27 
28 void
29 bfs()
30 {
31     int i, head, tail, nxt_x, nxt_y;
32     head = -1;
33     tail = 0;
34     queue[tail].x = start_x-1;
35     queue[tail].y = start_y-1;
36     visited[start_x-1][start_y-1= 1;
37     while(head < tail) {
38         ++head;
39         calculate(queue[head].x, queue[head].y);
40         for(i=0; i<8; i++) {
41             nxt_x = queue[head].x + dx[i];
42             nxt_y = queue[head].y + dy[i];
43             if(is_valid(nxt_x, nxt_y) && !visited[nxt_x][nxt_y] && grid[nxt_x][nxt_y]=='X') {
44                 ++tail;
45                 queue[tail].x = nxt_x;
46                 queue[tail].y = nxt_y;
47                 visited[nxt_x][nxt_y] = 1;
48             }
49         }
50     }
51 }
52 
53 int
54 main(int argc, char **argv)
55 {
56     int i;
57     while(scanf("%d %d %d %d"&row, &col, &start_x, &start_y) != EOF) {
58         if(row==0 && col==0)
59             break;
60         for(i=0; i<row; i++)
61             scanf("%s", grid[i]);
62         rt = 0;
63         memset(visited, 0sizeof(visited));
64         bfs();
65         printf("%d\n", rt);
66     }
67 }

posted @ 2010-08-19 14:41 simplyzhao 阅读(182) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=2244

参考:
http://baike.baidu.com/view/213217.html?fromTaglist

思路:
从这题认识了经典的约瑟夫问题:

无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。我们注意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程。因此如果要追求效率,就要打破常规,实施一点数学策略。
为了讨论方便,先把问题稍微改变一下,并不影响原意:

问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。

我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):
  k  k+1  k+2  ... n-2, n-1, 0, 1, 2, ... k-2
并且从k开始报0。

现在我们把他们的编号做一下转换:
k     --> 0
k+1   --> 1
k+2   --> 2
...
...
k-2   --> n-2
k-1   --> n-1

变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x'=(x+k)%n

如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是一个倒推问题!好了,思路出来了,下面写递推公式:

令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]

递推公式
f[1]=0;
f[i]=(f[i-1]+m)%i;  (i>1)

有了这个公式,我们要做的就是从1-n顺序算出f[i]的数值,最后结果是f[n]。因为实际生活中编号总是从1开始,我们输出f[n]+1

由于是逐级递推,不需要保存每个f[i],程序也是异常简单:

#include <stdio.h>
int main()
{
  int n, m, i, s=0;
  printf ("N M = "); scanf("%d%d", &n, &m);
  for (i=2; i<=n; i++) s=(s+m)%i;
  printf ("The winner is %d\n", s+1);
}

这个算法的时间复杂度为O(n),相对于模拟算法已经有了很大的提高。算n,m等于一百万,一千万的情况不是问题了。可见,适当地运用数学策略,不仅可以让编程变得简单,而且往往会成倍地提高算法执行效率。

代码:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 
 5 /*
 6  * f[1] = 0
 7  * f[i] = (f[i-1]+m)%i
 8  */
 9 int
10 josephus(int n, int m)
11 {
12     int i, s = 0;
13     for(i=2; i<=n; i++
14         s = (s+m)%i;
15     return s+1;
16 }
17 
18 int
19 main(int argc, char **argv)
20 {
21     int n, m;
22     while(scanf("%d"&n)!=EOF && n!=0) {
23         m = 2;
24         /* because first cut off city 1 and then begins every mth */
25         while(josephus(n-1, m) != 1)
26             ++m;
27         printf("%d\n", m);
28     }
29 }

posted @ 2010-08-18 21:02 simplyzhao 阅读(217) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1260

思路:
简单DP,但是就是想不到...

代码:
 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #define MAX_LEN 101
 5 #define INF 0x7FFFFFFF
 6 #define min(a,b) ((a)<(b) ? (a) : (b))
 7 int num[MAX_LEN], price[MAX_LEN];
 8 int sum[MAX_LEN]; /* aux */
 9 int table[MAX_LEN];
10 int c;
11 
12 /*
13  * f[i] represent the lowest possible price needed to buy list[1..i], so:
14  *      f[i] = min (f[k] + (num[k+1]++num[i]+10)*price[i]), 0<=k<i-1
15  */
16 int
17 dp()
18 {
19     int i, k, tmp;
20     sum[0= 0;
21     for(i=1; i<=c; i++)
22         sum[i] = sum[i-1]+num[i];
23     table[0= 0;
24     for(i=1; i<=c; i++) {
25         table[i] = INF;
26         for(k=0; k<=i-1; k++) {
27             tmp = table[k] + (sum[i]-sum[k]+10)*price[i];
28             table[i] = min(table[i], tmp);
29         }
30     }
31     return table[c];
32 }
33 
34 int
35 main(int argc, char **argv)
36 {
37     int i, tests;
38     scanf("%d"&tests);
39     while(tests--) {
40         scanf("%d"&c);
41         for(i=1; i<=c; i++)
42             scanf("%d %d", num+i, price+i);
43         printf("%d\n", dp());
44     }
45 }
posted @ 2010-08-18 09:33 simplyzhao 阅读(141) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1179

思路:
这题输在了对原题目的理解和分析上,没能成功地走出第一步:
                                                      将原题解析成对于表达式的求解
把缺了一条边的多边形展开成直线就是一个表达式
注意:为了求乘法,必须同时保存最大值和最小值

f[i][j]记录表达式中从顶点i到顶点j这段子表达式的值

动态规划的思想类似于矩阵连乘

参考:
http://hi.baidu.com/xiehuixb/blog/item/575ec81e221466c1a786699e.html

代码:
 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #define MAX_LEN 51
 5 #define INF 9223372036854775807LL
 6 #define maximal(a, b) ((a)>(b) ? (a) : (b))
 7 #define minimal(a, b) ((a)<(b) ? (a) : (b))
 8 char operand[MAX_LEN][2];
 9 int operators[MAX_LEN];
10 int n, ans_len, ans[MAX_LEN];
11 long long int rt, max[MAX_LEN][MAX_LEN], min[MAX_LEN][MAX_LEN];
12 
13 /*
14  * when it comes to: '+'
15  * max[i][j] = maximal(max[i][k] + max[k+1][j])
16  * min[i][j] = minimal(min[i][k] + min[k+1][j])
17  *
18  * when it comes to: '*'
19  * max[i][j] = maximal(max[i][k]*max[k+1][j], max[i][k]*min[k+1][j], min[i][k]*max[k+1][j], min[i][k]*min[k+1][j])
20  * min[i][j] = minimal(max[i][k]*max[k+1][j], max[i][k]*min[k+1][j], min[i][k]*max[k+1][j], min[i][k]*min[k+1][j])
21  *
22  * i<=k<j
23  */
24 void
25 solve(char *opd, int *ops, int del_edge)
26 {
27     int i, j, k, len;
28     for(i=1; i<=n; i++)
29         max[i][i] = min[i][i] = ops[i];
30     for(len=2; len<=n; len++) {
31         for(i=1; i<=n-len+1; i++) {
32             j = i+len-1;
33             max[i][j] = -INF;
34             min[i][j] = INF;
35             for(k=i; k<j; k++) {
36                 if(opd[k] == 't') {
37                     max[i][j] = maximal(max[i][j], max[i][k]+max[k+1][j]);
38                     min[i][j] = minimal(min[i][j], min[i][k]+min[k+1][j]);
39                 } else { /* opd[k] == 'x' */
40                     max[i][j] = maximal(max[i][j], max[i][k]*max[k+1][j]);
41                     max[i][j] = maximal(max[i][j], max[i][k]*min[k+1][j]);
42                     max[i][j] = maximal(max[i][j], min[i][k]*max[k+1][j]);
43                     max[i][j] = maximal(max[i][j], min[i][k]*min[k+1][j]);
44 
45                     min[i][j] = minimal(min[i][j], max[i][k]*max[k+1][j]);
46                     min[i][j] = minimal(min[i][j], max[i][k]*min[k+1][j]);
47                     min[i][j] = minimal(min[i][j], min[i][k]*max[k+1][j]);
48                     min[i][j] = minimal(min[i][j], min[i][k]*min[k+1][j]);
49                 }
50             }
51         }
52     }
53     if(max[1][n] >= rt) {
54         if(max[1][n] == rt)
55             ans[ans_len++= del_edge;
56         else {
57             rt = max[1][n];
58             ans_len = 0;
59             ans[ans_len++= del_edge;
60         }
61     }
62 }
63 
64 int
65 main(int argc, char **argv)
66 {
67     int i, j, k;
68     char tmpopd[MAX_LEN];
69     int tmpops[MAX_LEN];
70     while(scanf("%d"&n) != EOF) {
71         for(i=1; i<=n; i++)
72             scanf("%s%d", operand[i], operators+i);
73         rt = -INF;
74         ans_len = 0;
75         for(i=1; i<=n; i++) {
76             for(j=i%n+1, k=1; j!=i; j=j%n+1, k++)
77                 tmpopd[k] = operand[j][0];
78             tmpopd[k] = '\0';
79             tmpops[1= operators[i];
80             for(j=i%n+1, k=2; j!=i; j=j%n+1, k++)
81                 tmpops[k] = operators[j];
82             solve(tmpopd, tmpops, i);
83         }
84         printf("%lld\n", rt);
85         for(i=0; i<ans_len; i++)
86             printf("%d ", ans[i]);
87         printf("\n");
88     }
89 }
posted @ 2010-08-17 13:51 simplyzhao 阅读(132) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=2663

思路:
参考: http://www.tkz.org.ru/2009-07/poj-2663-tri-tiling/

递推题。经典。

本题是POJ2506 Tiling的威力加强版。由两行变成了三行。

推导过程与POJ2506异曲同工。

opt[i]=3*opt[i-2]+2*opt[i-4]+2*opt[i-6]+2*opt[i-8]+……直到方括号内表达式的值为0。

解释一下,3*opt[i-2]是最右边有三行2列的三种情况。

后面的2*opt[i-X],则是最右边有X列类似以下的结构的情况:

X=4列的情况:2663_1.jpg;X=6列的情况2663_2.JPG;等等等等

以上情况可以上下颠倒,故每种情况又有两种表示,所以需要乘以2。而以上的情况从4开始,然后每次递增2,所以递推式中这部分从i-4开始(如果大等于0的话),每次递减2。

如果i为奇数,稍微推一下,可得,奇数的列数无解,答案为0。

代码:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #define MAX_LEN 31
 5 long table[MAX_LEN];
 6 
 7 void
 8 build_table()
 9 {
10     int i, j, sum;
11     memset(table, 0sizeof(table));
12     table[0= 1;
13     table[2= 3;
14     for(i=4; i<MAX_LEN; i=i+2) {
15         sum = 3*table[i-2];
16         for(j=4; i-j>=0; j=j+2)
17             sum += (table[i-j]<<1);
18         table[i] = sum;
19     }
20 }
21 
22 int
23 main(int argc, char **argv)
24 {
25     int n;
26     build_table();
27     while(scanf("%d"&n)!=EOF && n!=-1) {
28         printf("%ld\n", table[n]);
29     }
30 }

posted @ 2010-08-16 10:44 simplyzhao 阅读(268) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1157

思路:
比较容易的动态规划,这里提供两种思路,其中第二种更优
另外,居然在写代码的时候将0xFFFFFFFF当作最小的int值,汗...

代码(方案1)
 1 /* 63MS */
 2 #include<stdio.h>
 3 #include<stdlib.h>
 4 #include<string.h>
 5 #define MAX_F 101
 6 #define MAX_V 101
 7 #define INF 0xF0000000
 8 int values[MAX_F][MAX_V];
 9 int table[MAX_F][MAX_V];
10 int f, v;
11 
12 /*
13  * f[i][j] represent the maximum aesthetic values for putting the first [1..i]
14  * bunches in the first [1..j] vases, so:
15  *      f[i][j] = max (f[i-1][k] + tmp), i-1<=k<j
16  */
17 dp()
18 {
19     int i, j, k, p, t, up, tmp, max;
20     table[1][1= values[1][1];
21     for(j=2; j<=v-f+1; j++/* i = 1 */
22         table[1][j] = values[1][j]>table[1][j-1? values[1][j] : table[1][j-1];
23     for(i=2; i<=f; i++) {
24         up = v-f+i;
25         for(j=i; j<=up; j++) {
26             max = INF;
27             for(k=i-1; k<j; k++) {
28                 t = values[i][k+1];
29                 for(p=k+2; p<j; p++)
30                     t = values[i][p] > t ? values[i][p] : t;
31                 tmp = table[i-1][k] + t;
32                 max = tmp > max ? tmp : max;
33             }
34             table[i][j] = max;
35         }
36     }
37     return table[f][v];
38 }
39 
40 int
41 main(int argc, char **argv)
42 {
43     int i, j;
44     while(scanf("%d %d"&f, &v)!=EOF) {
45         for(i=1; i<=f; i++)
46             for(j=1; j<=v; j++)
47                 scanf("%d", values[i]+j);
48         printf("%d\n", dp());
49     }
50 }

代码(方案2)
 1 /* 32MS */
 2 #include<stdio.h>
 3 #include<stdlib.h>
 4 #include<string.h>
 5 #define MAX_F 101
 6 #define MAX_V 101
 7 #define INF 0xF0000000
 8 int values[MAX_F][MAX_V];
 9 int table[MAX_F][MAX_V];
10 int f, v;
11 
12 /*
13  * f[i][j] represent the maximum aesthetic values for putting the i(th) bunch 
14  * in the j(th) vases(total i bunches), so:
15  *      f[i][j] = max(f[i-1][k])+values[i][j], i-1<=k<j
16  */
17 dp()
18 {
19     int i, j, k, up, max, rt;
20     for(j=1; j<=v-f+1; j++
21         table[1][j] = values[1][j];
22     for(i=2; i<=f; i++) {
23         up = v-f+i;
24         for(j=i; j<=up; j++) {
25             max = INF;
26             for(k=i-1; k<j; k++
27                 max = table[i-1][k] > max ? table[i-1][k] : max;
28             table[i][j] = max+values[i][j];
29         }
30     }
31     rt = INF;
32     for(j=f; j<=v; j++)
33         rt = table[f][j]>rt ? table[f][j] : rt;
34     return rt;
35 }
36 
37 int
38 main(int argc, char **argv)
39 {
40     int i, j;
41     while(scanf("%d %d"&f, &v)!=EOF) {
42         for(i=1; i<=f; i++)
43             for(j=1; j<=v; j++)
44                 scanf("%d", values[i]+j);
45         printf("%d\n", dp());
46     }
47 }
posted @ 2010-08-15 11:16 simplyzhao 阅读(106) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1731

思路:
求全排列,这个问题本身挺简单,不过题目要求: a. 不能重复;b. 有序输出
记得曾经做过这题,当时偷懒用STL过的(真要自己写,估计当时也不会(*^__^*) 嘻嘻……)
现在,决心弃用C++来做题,好好锻炼基本功,所以就硬着头皮自己慢慢写
好在前段时间对于搜索题有了一定的积累,否则相信自己肯定还是不知道怎么写的
找个例子,画出递归调用树,对于理解有很大帮助

纯C递归实现如下:
代码:
 1 /* 364K 454MS */
 2 #include<stdio.h>
 3 #include<stdlib.h>
 4 #include<string.h>
 5 #define MAX_LEN 201
 6 char str[MAX_LEN];
 7 int len;
 8 
 9 int
10 compare(const void *arg1, const void *arg2) /* for qsort */
11 {
12     return *((char *)arg1) - *((char *)arg2);
13 }
14 
15 void
16 swap(char *seq, int i, int j) /* exchange */
17 {
18     char tmp = seq[i];
19     seq[i] = seq[j];
20     seq[j] = tmp;
21 }
22 
23 void
24 perm(char *seq, int begin, int end)
25 {
26     int i, j, tmp;
27     char pre=0;
28     if(begin >= end) {
29         printf("%s\n", seq);
30         return;
31     }
32     for(i=begin; i<=end; i++) {
33         if(i>begin && seq[i]==seq[begin]) /* avoid duplicates */
34             continue;
35         if(pre == seq[i]) /* avoid duplicate */
36             continue;
37         /* in order to keep the alphabetical order */
38         tmp = seq[i];
39         for(j=i; j>begin; j--)
40             seq[j] = seq[j-1];
41         seq[begin] = tmp;
42         perm(seq, begin+1, end);
43         tmp = seq[begin];
44         for(j=begin; j<i; j++)
45             seq[j] = seq[j+1];
46         seq[i] = tmp;
47         /*
48         swap(seq, begin, i);
49         perm(seq, begin+1, end);
50         swap(seq, begin, i);
51         */
52         pre = seq[i];
53     }
54 }
55 
56 int
57 main(int argc, char **argv)
58 {
59     while(scanf("%s", str) != EOF) {
60         len = strlen(str);
61         qsort(str, len, sizeof(char), compare);
62         perm(str, 0, len-1);
63     }
64 }



posted @ 2010-08-15 09:10 simplyzhao 阅读(110) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1631

思路:
题意理解清楚,其实就是找出最长上升子序列
这里采用O(nlogn)的算法,类似于贪心的原理,关键是要理解辅助数组aux[]的含义,aux[len]所代表的是组成长度为len的最长上升子序列的尾元素的最小值

下面的内容转自: http://blog.csdn.net/ottoCho/archive/2009/12/02/4927262.aspx
O(n*log n)算法分析如下:

设 A[t]表示序列中的第t个数,F[t]表示从1到t这一段中以t结尾的最长上升子序列的长度,初始时设F [t] = 0(t = 1, 2, ..., len(A))。则有动态规划方程:F[t] = max{1, F[j] + 1} (j = 1, 2, ..., t - 1, 且A[j] < A[t])。 

现在,我们仔细考虑计算F[t]时的情况。假设有两个元素A[x]和A[y],满足 
(1)x < y < t  (这里应该错了,如果x<y<t成立,那么F[y]>=F[x]+1,不可能有(3)成立,这里应该是y<x<t) [by simplyzhao, 2010-10-19]
(2)A[x] < A[y] < A[t] 
(3)F[x] = F[y] 
此时,选择F[x]和选择F[y]都可以得到同样的F[t]值,那么,在最长上升子序列的这个位置中,应该选择A[x]还是应该选择A[y]呢? 
很明显,选择A[x]比选择A[y]要好。因为由于条件(2),在A[x+1] ... A[t-1]这一段中,如果存在A[z],A[x] < A[z] < a[y],则与选择A[y]相比,将会得到更长的上升子序列。 
再根据条件(3),我们会得到一个启示:根据F[]的值进行分类。对于F[]的每一个取值k,我们只需要保留满足F[t] = k的所有A[t]中的最小值。设D[k]记录这个值,即D[k] = min{A[t]} (F[t] = k)。 

注意到D[]的两个特点: 
(1) D[k]的值是在整个计算过程中是单调不上升的。 
(2) D[]的值是有序的,即D[1] < D[2] < D[3] < ... < D[n]。 

利 用D[],我们可以得到另外一种计算最长上升子序列长度的方法。设当前已经求出的最长上升子序列长度为len。先判断A[t]与D[len]。若A [t] > D[len],则将A[t]接在D[len]后将得到一个更长的上升子序列,len = len + 1, D[len] = A [t];否则,在D[1]..D[len]中,找到最大的j,满足D[j] < A[t]。令k = j + 1,则有A [t] <= D[k],将A[t]接在D[j]后将得到一个更长的上升子序列,更新D[k] = A[t]。最后,len即为所要求的最长上 升子序列的长度。 

在 上述算法中,若使用朴素的顺序查找在D[1]..D[len]查找,由于共有O(n)个元素需要计算,每次计算时的复杂度是O(n),则整个算法的 时间复杂度为O(n^2),与原来的算法相比没有任何进步。但是由于D[]的特点(2),我们在D[]中查找时,可以使用二分查找高效地完成,则整个算法 的时间复杂度下降为O(nlogn),有了非常显著的提高。需要注意的是,D[]在算法结束后记录的并不是一个符合题意的最长上升子序列!

代码:
 1 /* O(nlogn) algorithm: the longest increasing sub-sequence [LIS] */
 2 #include<stdio.h>
 3 #include<stdlib.h>
 4 #include<string.h>
 5 #define MAX_LEN 40001
 6 int num[MAX_LEN];
 7 int aux[MAX_LEN];
 8 int size, rt_len;
 9 
10 int
11 dp()
12 {
13     int i, left, right, mid;
14     rt_len = 1;
15     aux[rt_len] = num[0];
16     for(i=1; i<size; i++) {
17         if(num[i] > aux[rt_len]) {
18             ++rt_len;
19             aux[rt_len] = num[i];
20         } else {
21             /* binary search: O(logn) */
22             left = 1;
23             right = rt_len;
24             while(left <= right) {
25                 mid = (left+right)/2;
26                 if(num[i]>aux[mid])
27                     left = mid+1;
28                 else
29                     right = mid-1;
30             }
31             aux[left] = num[i];
32         }
33     }
34     return rt_len;
35 }
36 
37 int
38 main(int argc, char **argv)
39 {
40     int i, tests;
41     scanf("%d"&tests);
42     while(tests--) {
43         scanf("%d"&size);
44         for(i=0; i<size; i++)
45             scanf("%d", num+i);
46         printf("%d\n", dp());
47     }
48 }
posted @ 2010-08-14 21:09 simplyzhao 阅读(335) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1887
http://acm.pku.edu.cn/JudgeOnline/problem?id=2533

思路:
典型而简单的动态规划,类似于最大子段和的思想:

                             f[i]表示以num[i]结尾的最长下降(上升)子序列,那么:
                                  f[i] = max (f[j]+1, if num[j]>num[i] && 0<=j<i)
原本挺简单的代码,一会就写完了,结果却因为一个临时变量的初始化错误而WA了好多次,要细心...
上述是O(n^2)的算法,另外还有O(nlgn)的算法,下回尝试。

代码(pku 1887):
 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #define MAX_LEN 32767
 5 int num[MAX_LEN];
 6 int max[MAX_LEN];
 7 int len;
 8 
 9 /*
10  * f[i] represent the longest descent sequence ended with num[i], so:
11  *      f[i] = max ( f[j]+1, if num[j]>num[i], 0<=j<i)
12  */
13 int
14 dp()
15 {
16     int i, j, t, rt;
17     max[0= rt = 1;
18     for(i=1; i<len; i++) {
19         t = 1/* WA twice here, for t=-1 */
20         for(j=0; j<i; j++) {
21             if(num[j] > num[i])
22                 t = max[j]+1>? max[j]+1 : t;
23         }
24         max[i] = t;
25         rt = max[i]>rt ? max[i] : rt;
26     }
27     return rt;
28 }
29 
30 int
31 main(int argc, char **argv)
32 {
33     int i, tmp, cnt = 0;
34     while(scanf("%d"&tmp)!=EOF && tmp!=-1) {
35         num[0= tmp;
36         len = 1;
37         while(scanf("%d", num+len)!=EOF && num[len]!=-1)
38             ++len;
39         printf("Test #%d:\n  maximum possible interceptions: %d\n\n"++cnt, dp());
40     }
41 }
posted @ 2010-08-14 11:04 simplyzhao 阅读(180) | 评论 (0)编辑 收藏
仅列出标题
共21页: First 10 11 12 13 14 15 16 17 18 Last 

导航

<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

统计

常用链接

留言簿(1)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜