问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=2192思路:
原本以为是类似于PKU 1936的简单题,结果Sample测试不过,发现对于像cat, tree这样包含相同字母(这里是t)的例子需要回溯,于是DFS,这样结果虽然正确了,但是却TLE...
准确的做法是动态规划,艾,今天三题没有一个是自己想出来的...悲剧...
详细的状态转化方程见代码注释
代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #define MAX_LEN 201
5 char first[MAX_LEN+1], second[MAX_LEN+1];
6 char final[MAX_LEN*2];
7 int flen, slen, tlen;
8 int table[MAX_LEN][MAX_LEN];
9
10 /*
11 * f[i][j] represent whether final[1..i+j] could be formed from first[1..i] and second[1..j]
12 * f[i][j] is true if:
13 * a. final[i+j]==first[i] && f[i-1][j] is true, or
14 * b. final[i+j]==second[j] && f[i][j-1] is true
15 */
16 int
17 dp()
18 {
19 int i, j, mark;
20 mark = 1;
21 for(i=1; i<=flen; i++) {
22 if(first[i]==final[i] && mark)
23 table[i][0] = 1;
24 else {
25 table[i][0] = 0;
26 mark = 0;
27 }
28 }
29 mark = 1;
30 for(j=1; j<=slen; j++) {
31 if(second[j]==final[j] && mark)
32 table[0][j] = 1;
33 else {
34 table[0][j] = 0;
35 mark = 0;
36 }
37 }
38 for(i=1; i<=flen; i++) {
39 for(j=1; j<=slen; j++) {
40 if((final[i+j]==first[i]&&table[i-1][j]) || (final[i+j]==second[j]&&table[i][j-1]))
41 table[i][j] = 1;
42 else
43 table[i][j] = 0;
44 }
45 }
46 return table[flen][slen];
47 }
48
49 int
50 main(int argc, char **argv)
51 {
52 int tests, cnt=0;
53 scanf("%d", &tests);
54 while(tests--) {
55 scanf("%s %s %s", first+1, second+1, final+1);
56 flen = strlen(first+1);
57 slen = strlen(second+1);
58 tlen = strlen(final+1);
59 printf("Data set %d: %s\n", ++cnt, dp()?"yes":"no");
60 }
61 }
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1080思路:
想法与最长公共子序列类似
用f[i][j]表示str_a[1..i]与str_b[1..j]的相似度,而所求即f[len_a][len_b],需要注意初始化部分的代码
状态转移方程见代码注释
代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #define MAX_LEN 101
5 #define INF 'I'
6 #define Score(ch_a, ch_b) (score[map[ch_a-'A']][map[ch_b-'A']])
7 int len_a, len_b;
8 char str_a[MAX_LEN+1], str_b[MAX_LEN+1];
9 int table[MAX_LEN][MAX_LEN];
10 const int score[][5] = {{5,-1,-2,-1,-3}, {-1,5,-3,-2,-4}, {-2,-3,5,-2,-2}, {-1,-2,-2,5,-1}, {-3,-4,-2,-1,-65535}};
11 /* A C G I T */
12 const int map[] = {0,-1,1,-1,-1,-1,2,-1,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3};
13
14 /*
15 * f[i][j] represent the similarity of str_a[1..i] and str_b[1..j], so:
16 * f[i][j] = max { f[i-1][j-1]+Score(str_a[i], str_b[j]),
17 * f[i-1][j] + Score(str_a[i], '-'),
18 * f[i][j-1] + Score('-', str_b[j]) }
19 */
20 int
21 dp()
22 {
23 int i, j, a, b, c, max;
24 /* Attention: Initialization */
25 table[0][0] = 0;
26 for(i=1; i<=len_a; i++)
27 table[i][0] = table[i-1][0] + Score(str_a[i], INF);
28 for(j=1; j<=len_b; j++)
29 table[0][j] = table[0][j-1] + Score(str_b[j], INF);
30
31 for(i=1; i<=len_a; i++) {
32 for(j=1; j<=len_b; j++) {
33 a = table[i-1][j-1] + Score(str_a[i], str_b[j]);
34 b = table[i-1][j] + Score(str_a[i], INF);
35 c = table[i][j-1] + Score(INF, str_b[j]);
36 max = a > b ? a : b;
37 max = c > max ? c : max;
38 table[i][j] = max;
39 }
40 }
41 return table[len_a][len_b];
42 }
43
44 int
45 main(int argc, char **argv)
46 {
47 int tests;
48 scanf("%d", &tests);
49 while(tests--) {
50 scanf("%d %s", &len_a, str_a+1);
51 scanf("%d %s", &len_b, str_b+1);
52 printf("%d\n", dp());
53 }
54 }
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1160思路:
事先知道这是动态规划的题,于是就拼命往这方面想,想啊想啊想啊想,终于灵感一现:
可以用f[i][j]表示到第i个村镇为止,建造j个邮局所需要的最小距离
心中窃喜,感觉应该挺靠谱,于是继续深入,直觉告诉我f[i][j]与f[i-1][j], f[i-1][j-1]应该有关系,貌似成了第i个村镇建不建邮局的子问题,继续苦思冥想,结果却还是想不出来。
无奈,还是看别人的思路吧:
用f[i][j]表示前i个邮局建在前j个村镇所需要的最小距离(貌似,跟我之前想的刚好相反)
f[i][j] = min( f[i-1][k] + cost[k+1][j], i-1<=k<=j-1),cost[i][j]表示在村镇i与j之间建造一个post office的最小距离(人家说显然在中点)
艾,差之毫厘,谬以千里,如何有效地表示和分析最优子结构是关键:一个问题的解,如何通过其子问题来表示和求解
代码:
1 /* 752K 79MS */
2 #include<stdio.h>
3 #include<stdlib.h>
4 #include<string.h>
5 #define MAX_V 301
6 #define MAX_P 31
7 #define INF 0x7FFFFFFF
8 int v, p;
9 int pos[MAX_V];
10 int cost[MAX_V][MAX_V];
11 int table[MAX_P][MAX_V];
12
13 void
14 init()
15 {
16 int i, j, k, mid, diff;
17 for(i=1; i<=v; i++) {
18 for(j=i; j<=v; j++) {
19 diff = 0;
20 mid = (i+j)/2;
21 for(k=i; k<=j; k++)
22 diff += ((pos[k]>pos[mid])?(pos[k]-pos[mid]):(pos[mid]-pos[k]));
23 cost[i][j] = cost[j][i] = diff;
24 }
25 }
26 }
27
28 int
29 dp()
30 {
31 int i, j, k, min, tmp;
32 memset(table, 0, sizeof(table));
33 for(j=1; j<=v; j++)
34 table[1][j] = cost[1][j];
35 for(i=2; i<=p; i++) {
36 for(j=i+1; j<=v; j++) {
37 min = INF;
38 for(k=i-1; k<=j-1; k++) {
39 tmp = table[i-1][k] + cost[k+1][j];
40 min = tmp<min ? tmp : min;
41 }
42 table[i][j] = min;
43 }
44 }
45 return table[p][v];
46 }
47
48 int
49 main(int argc, char **argv)
50 {
51 int i;
52 while(scanf("%d %d", &v, &p)!=EOF) {
53 for(i=1; i<=v; i++)
54 scanf("%d", pos+i);
55 init();
56 printf("%d\n", dp());
57 }
58 }
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1159思路:
看完题目,完全不知道该怎么做
参考discuss,发现原来DP这么这么地强大,自己那是相当菜
设ch[1]..ch[n]表示字符串1至n位,i为左游标,j为右游标 ,则i从n递减,j从i开始递增。
min[i][j]表示i和j之间至少需要插入多少个字符才能对称,初始置全0 ,我们最终需要得到的值是min[1][n].
则
if(ch[i]==ch[j]) //如果两个游标所指字符相同,向中间缩小范围
min[i][j]=min[i+1][j-1];
else
min[i][j] = 1 +(min[i+1][j]和min[i][j-1]中的较小值); //如果不同,典型的状态转换方程
额,状态方程看明白了,结果发现代码居然不知道怎么写...那个汗哪...
原因是没有发现当i>j时,min[i][j] = 0
代码(记忆化搜索):
1 /* Time: 1594MS */
2 #include<stdio.h>
3 #include<stdlib.h>
4 #include<string.h>
5 #define MAX_LEN 5001
6 char str[MAX_LEN+1];
7 int len;
8 short memory[MAX_LEN][MAX_LEN];
9
10 /*
11 * f[i][j] represent the minimal insert for str[i..j]
12 *
13 * f[i][j] = f[i+1][j-1], if str[i]==str[j], otherwise
14 * f[i][j] = min(f[i+1][j], f[i][j-1]) + 1
15 */
16 int
17 func(int i, int j)
18 {
19 int a, b;
20 if(i >= j)
21 return 0;
22 if(memory[i][j])
23 return memory[i][j];
24 if(str[i] == str[j])
25 memory[i][j] = func(i+1, j-1);
26 else {
27 a = func(i+1, j);
28 b = func(i, j-1);
29 memory[i][j] = a<b ? a+1 : b+1;
30 }
31 return memory[i][j];
32 }
33
34 int
35 main(int argc, char **argv)
36 {
37 while(scanf("%d", &len) != EOF) {
38 memset(memory, 0, sizeof(memory));
39 scanf("%s", str+1);
40 printf("%d\n", func(1, len));
41 }
42 }
代码(dp):
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #define MAX_LEN 5001
5 char str[MAX_LEN];
6 int len;
7 short table[MAX_LEN][MAX_LEN];
8
9 short
10 dp()
11 {
12 int i, j;
13 /* if i>j, then table[i][j] = 0 */
14 for(i=len-1; i>=1; i--) {
15 for(j=i; j<=len; j++) {
16 if(str[i] == str[j])
17 table[i][j] = table[i+1][j-1];
18 else
19 table[i][j] = table[i+1][j]<table[i][j-1] ? table[i+1][j]+1 : table[i][j-1]+1;
20 }
21 }
22 return table[1][len];
23 }
24
25 int
26 main(int argc, char **argv)
27 {
28 while(scanf("%d", &len) != EOF) {
29 memset(table, 0, sizeof(table));
30 scanf("%s", str+1);
31 printf("%d\n", dp());
32 }
33 }
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=2250思路:
以单词为单位的LCS,另外需要记录路径信息
代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #define MAX_NUM 101
5 #define MAX_LEN 31
6 char first[MAX_NUM][MAX_LEN];
7 char second[MAX_NUM][MAX_LEN];
8 int fcnt, scnt;
9 int table[MAX_NUM][MAX_NUM];
10 char path[MAX_NUM][MAX_NUM];
11
12 void
13 output(int i, int j)
14 {
15 if(i==0 || j==0)
16 return;
17 if(path[i][j] == 'u')
18 output(i-1, j);
19 else if(path[i][j] == 'l')
20 output(i, j-1);
21 else {
22 output(i-1, j-1);
23 printf("%s ", first[i-1]);
24 }
25 }
26
27 void
28 lcs()
29 {
30 int i, j;
31 for(i=0; i<=fcnt; i++)
32 table[i][0] = 0;
33 for(j=0; j<=scnt; j++)
34 table[0][j] = 0;
35 for(i=1; i<=fcnt; i++) {
36 for(j=1; j<=scnt; j++) {
37 if(strcmp(first[i-1], second[j-1]) == 0) {
38 table[i][j] = table[i-1][j-1] + 1;
39 path[i][j] = 'y';
40 } else if(table[i-1][j] > table[i][j-1]) {
41 table[i][j] = table[i-1][j];
42 path[i][j] = 'u';
43 } else {
44 table[i][j] = table[i][j-1];
45 path[i][j] = 'l';
46 }
47 }
48 }
49 }
50
51 int
52 main(int argc, char **argv)
53 {
54 while(1) {
55 fcnt = scnt = 0;
56 memset(table, 0, sizeof(table));
57 memset(path, 0, sizeof(path));
58 if(scanf("%s", first[fcnt++]) == EOF)
59 break;
60 while(scanf("%s", first[fcnt]) && first[fcnt][0]!='#')
61 ++fcnt;
62 while(scanf("%s", second[scnt]) && second[scnt][0]!='#')
63 ++scnt;
64 lcs();
65 output(fcnt, scnt);
66 printf("\n");
67 }
68 }
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1953思路:
如果想通了,其实就是道挺简单的动规题
zero_end[i]记录第i位以0结尾的合法排列
one_end[i]记录第i位以1结尾的合法排列
状态方程即:
zero_end[i+1] = zero_end[i] + one_end[i]
one_end[i+1] = zero_end[i]
以深度搜索的递归树即可观察得出以上结论(*^__^*) 嘻嘻……
代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #define MAX_LEN 45
5 int zero_end[MAX_LEN+1];
6 int one_end[MAX_LEN+1];
7
8 int
9 build_table()
10 {
11 int i;
12 zero_end[1] = 1;
13 one_end[1] = 1;
14 for(i=2; i<=45; i++) {
15 zero_end[i] = zero_end[i-1] + one_end[i-1];
16 one_end[i] = zero_end[i-1];
17 }
18 }
19
20 int
21 main(int argc, char **argv)
22 {
23 int i, n, tests;
24 build_table();
25 scanf("%d", &tests);
26 for(i=1; i<=tests; i++) {
27 scanf("%d", &n);
28 printf("Scenario #%d:\n%d\n\n", i, zero_end[n]+one_end[n]);
29 }
30 }
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1163思路:
比较基础、容易的动态规划
table[i][j]记录从根节点到第i行第j个元素的最大和
状态转移方程:
table[i][j] = max(table[i][j-1], table[i][j]) + num[i][j]
为了节省空间,对于三角形的保存,采用了一维数组
代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #define MAX_N 101
5 #define MAX_LEN 5051 /* 1+2+3++100 */
6 int rows, num[MAX_LEN];
7 int table[MAX_N][MAX_N];
8
9 /* table[i][j] = max(table[i-1][j-1]+num[i][j], table[i-1][j]+num[i][j] */
10 int
11 dp()
12 {
13 int i, j, k, tmp, max, rt;
14 table[0][0] = num[0];
15 for(i=1; i<rows; i++) {
16 tmp = i*(i+1)/2;
17 for(j=0; j<=i; j++) {
18 max = 0;
19 for(k=1; k>=0; k--)
20 if(j-k>=0 && j-k<=i-1) /* parent index: [i-1][j-k] */
21 max = table[i-1][j-k]+num[tmp+j]>max ? table[i-1][j-k]+num[tmp+j] : max;
22 table[i][j] = max;
23 }
24 }
25 rt = 0;
26 for(j=0; j<rows; j++)
27 if(table[rows-1][j] > rt)
28 rt = table[rows-1][j];
29 return rt;
30 }
31
32 int
33 main(int argc, char **argv)
34 {
35 int i, j, tmp;
36 while(scanf("%d", &rows) != EOF) {
37 for(i=0; i<rows; i++) {
38 tmp = i*(i+1)/2;
39 for(j=0; j<=i; j++)
40 scanf("%d", num+(tmp+j));
41 }
42 memset(table, 0, sizeof(table));
43 printf("%d\n", dp());
44 }
45 }
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1276思路:
典型的多重背包问题,参考背包九讲,一次AC
深刻理解:01背包、完全背包、多重背包
代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #define MAX_VOLUMN 100001
5 #define MAX_N 11
6 #define max(a,b) ((a)>(b) ? (a) : (b))
7 int vol, n;
8 int cv[MAX_N], num[MAX_N], f[MAX_VOLUMN];
9
10 /* f[i][v] = max(f[i-1][v], f[i-1][v-cost[i]]+value[i]) */
11 void
12 ZeroOnePack(int cost, int value)
13 {
14 int v;
15 for(v=vol; v>=cost; v--)
16 f[v] = max(f[v], f[v-cost]+value);
17 }
18
19 /* f[i][v] = max(f[i-1][v], f[i][v-cost[i]]+value[i]) */
20 void
21 CompletePack(int cost, int value)
22 {
23 int v;
24 for(v=cost; v<=vol; v++)
25 f[v] = max(f[v], f[v-cost]+value);
26 }
27
28 void
29 MultiplePack(int cost, int value, int amount)
30 {
31 int k = 1;
32 if(cost*amount >= vol)
33 CompletePack(cost, value);
34 else {
35 while((amount-k) >= 0) {
36 ZeroOnePack(cost*k, value*k);
37 amount -= k;
38 k = k*2;
39 }
40 ZeroOnePack(cost*amount, value*amount);
41 }
42 }
43
44 void
45 dp()
46 {
47 int i;
48 for(i=1; i<=n; i++)
49 MultiplePack(cv[i], cv[i], num[i]);
50 }
51
52 int
53 main(int argc, char **argv)
54 {
55 int i;
56 while(scanf("%d %d", &vol, &n)!=EOF) {
57 memset(f, 0, sizeof(f));
58 for(i=1; i<=n; i++)
59 scanf("%d %d", num+i, cv+i);
60 dp();
61 printf("%d\n", f[vol]);
62 }
63 }
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=3624思路:
典型的01背包问题,动态规划,参考背包九讲
注意需要使用滚动数组,否则MLE
代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #define MAX_N 3403
5 #define MAX_M 12881
6 #define max(a,b) ((a)>(b) ? (a) : (b))
7 int n, m;
8 /* MLE: int table[MAX_N][MAX_M]; */
9 int table[MAX_M];
10 int weight[MAX_N], rate[MAX_N];
11
12 void
13 dp()
14 {
15 int i, j;
16 memset(table, 0, sizeof(table));
17 for(i=1; i<=n; i++)
18 for(j=m; j>=weight[i]; j--) /* Attention: descent order */
19 table[j] = max(table[j], table[j-weight[i]]+rate[i]);
20 }
21
22 int
23 main(int argc, char **argv)
24 {
25 int i;
26 while(scanf("%d %d", &n, &m)!=EOF) {
27 for(i=1; i<=n; i++)
28 scanf("%d %d", weight+i, rate+i);
29 dp();
30 printf("%d\n", table[m]);
31 }
32 }