A Za, A Za, Fighting...

坚信:勤能补拙

问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1028

思路:
这是道简单题,1次AC呵呵
最简单的做法莫过于直接用两个堆栈根据题目的description进行模拟
不过,这里,我只用了一个数组来模拟所有的动作
需要注意的一点是: FORWARD的动作只有在之前存在BACK动作的条件下才有可能有效
 1 #define LEN_MAX 71
 2 #define NUM_MAX 100
 3 #define CMD_MAX 8
 4 #define QUIT "QUIT"
 5 #define FORWARD "FORWARD"
 6 #define VISIT "VISIT"
 7 #define BACK "BACK"
 8 #define IGN "Ignored"
 9 char cmd[CMD_MAX];
10 char addr[LEN_MAX];
11 char arr[NUM_MAX][LEN_MAX];
12 int cur_pos, bk_pos, fw_pos;
13 int can_fw_count;
14 
15 int 
16 main(int argc, char **argv)
17 {
18     cur_pos = can_fw_count = 0;
19     bk_pos = fw_pos = -1;
20     strcpy(arr[cur_pos], "http://www.acm.org/");
21     while(scanf("%s", cmd)!=EOF && strcmp(cmd, QUIT)!=0) {
22         if(strcmp(cmd, VISIT)==0) {
23             scanf("%s", addr);
24             strcpy(arr[++cur_pos], addr);
25             bk_pos = cur_pos - 1;
26             can_fw_count = 0;
27             printf("%s\n", arr[cur_pos]);
28         } else if(strcmp(cmd, BACK)==0) {
29             if(bk_pos == -1)
30                 printf("%s\n", IGN);
31             else {
32                 fw_pos = cur_pos;
33                 cur_pos = bk_pos;
34                 bk_pos = cur_pos-1;
35                 ++can_fw_count;
36                 printf("%s\n", arr[cur_pos]);
37             }
38         } else if(strcmp(cmd, FORWARD)==0) {
39             if(fw_pos == -1 || !can_fw_count)
40                 printf("%s\n", IGN);
41             else {
42                 bk_pos = cur_pos;
43                 cur_pos = fw_pos;
44                 fw_pos = cur_pos+1;
45                 --can_fw_count;
46                 printf("%s\n", arr[cur_pos]);
47             }
48         }
49     }
50     return 0;
51 }

posted @ 2010-07-04 09:45 simplyzhao 阅读(204) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1015

思路:
最小差最大和问题

这题...一点想法都没有,就是不会写,只好上网搜索人家的思路,总结如下
动态规划状态方程:
    f(j, k) = f(j-1, x), x+(di-pi) = k
这里,f(j, k)表示选择了j个人,其最小差为k且满足最大和的解
另外,还需要记录已选择了哪些人,因为在状态迁移的过程中,只能选择之前未被选择过的人

 1     int base = m*MAX_GRADE;  /* 防止出现负数 */
 2     memset(f, -1sizeof(f));
 3     memset(sum, -1sizeof(sum));
 4     /* initialize */
 5     for(i=1; i<=n; i++)
 6         if(sum[1][minus[i]+base< plus[i]) {
 7             f[1][minus[i]+base= i;
 8             sum[1][minus[i]+base= plus[i];
 9         }
10     for(j=2; j<=m; j++) {
11         for(k=0; k<=2*m*MAX_GRADE; k++) {
12             if(f[j-1][k] != -1) {
13                 for(i=1; i<=n; i++) {
14                     /* see if i has been used */
15                     q = k;
16                     for(p=j-1; p>=1; p--) {
17                         if(f[p][q] == i)
18                             break;
19                         q -= minus[f[p][q]];
20                     }
21                     if(p<1) {
22                         if(sum[j][k+minus[i]] < sum[j-1][k]+plus[i]) {
23                             f[j][k+minus[i]] = i;
24                             sum[j][k+minus[i]] = sum[j-1][k]+plus[i];
25                         }
26                     }
27                 }
28             }
29         }
30     }

    
posted @ 2010-07-04 09:34 simplyzhao 阅读(189) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1088

思路1:
这题是前段时间微软笔试的最后一道大题,当时没想太多,直接简单DFS,没想到会超时,结果嘛直接被BS了...太菜啊
我们从最优解开始分析:
      设p[1]--p[2]--p[3]...--p[n]即为最长的一条路径L, p[i]=(xi, yi)
      对于该路径L中的一个点p[i], 可以这样来理解: 到达点p[i]的最长路径是到达点p[i-1]的最长路径加1, 并且height(p[i-1])大于height(p[i])
      因此,我们可以先将输入地图按照高度从高到低排序,然后从头开始依次求出最长路径
需要注意的一点:
下面代码的第8行需要设置max为1,而不是0, 因为该点可能是最高点(peek)
 1 int 
 2 dp()
 3 {
 4     int total = row*col;
 5     int i, j, x, y, sx, sy, td, max, longest=1;
 6     distance[points[0].x][points[0].y] = 1//highest point
 7     for(i=1; i<total; i++) {
 8         max = 1//max should be set 1, in case points[i] is a peek
 9         x = points[i].x;
10         y = points[i].y;
11         for(j=0; j<4; j++) { //four directions
12             sx = x+dx[j];
13             sy = y+dy[j];
14             //points[sx*col+sy] is a higher point around points[i]
15             if(can_go(sx, sy) && points[i].height<height[sx*col+sy]) { //distance[sx][sy]>0 indicates (sx, sy) a higher point
16                 td = distance[sx][sy]+1;
17                 max = max > td ? max : td;
18             }
19         }
20         distance[x][y] = max;
21         longest = longest > max ? longest : max;
22     }
23     return longest;
24 }

思路2:
备忘录方法
这里我们换一种看待该问题的方式
该题有一个很自然的想法,那就是依次枚举每个点,计算从每个点出发的最长路径,最后求这些最长路径的最大值即可
从一个点p[i]出发的最长路径是: 从其上下左右四个点出发的最长路径的最大值加1

备忘录方法真的非常好用,而且理解起来也较动态规划简单呵呵,原本超时的代码只要稍加修改就可以AC了
 1 int
 2 dp_memory(int x, int y)
 3 {
 4     if(opt[x][y] != 0//memory, simple but powerful
 5         return opt[x][y];
 6 
 7     int max = 0;
 8     int i, sx, sy, tmp;
 9     for(i=0; i<4; i++) { // four directions
10         sx = x + dx[i];
11         sy = y + dy[i];
12         if(sx>=0 && sx<=row-1 && sy>=0 && sy<=col-1 && map[sx][sy]<map[x][y]) {
13             tmp = dp_memory(sx, sy);
14             max = max > tmp ? max : tmp;
15         }
16     }
17     opt[x][y] = max+1;
18     return opt[x][y];
19 }
1 for(i=0; i<row; i++)
2         for(j=0; j<col; j++) {
3             tmp = dp_memory(i, j);
4             max = max > tmp ? max : tmp;
5         }
6 
posted @ 2010-06-29 23:56 simplyzhao 阅读(219) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1579

思路:
根据题意的描述,采用递归是显然易见的
不过,该题的另一个突出的特点是重复子问题,如何既可以获得递归的简洁,又同时可以避免重复子问题的多次计算呢?
这时,就可以采用备忘录方法

 1 #define MAX 21
 2 long table[MAX][MAX][MAX];
 3 
 4 long 
 5 function_run_fun(int a, int b, int c)
 6 {
 7     if(a<=0 || b<=0 || c<=0)
 8         return 1;
 9     if(a>20 || b>20 || c>20
10         return (table[20][20][20= function_run_fun(202020));
11 
12     if(table[a][b][c] != 0//memory search
13         return table[a][b][c];
14 
15     else if(a<&& b<c)
16         table[a][b][c] = function_run_fun(a, b, c-1+ function_run_fun(a, b-1, c-1- function_run_fun(a, b-1, c);
17     else
18         table[a][b][c] = function_run_fun(a-1, b, c) + function_run_fun(a-1, b-1, c) + function_run_fun(a-1, b, c-1- function_run_fun(a-1, b-1, c-1);
19     return table[a][b][c];
20 }

posted @ 2010-06-29 22:52 simplyzhao 阅读(180) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=3356

思路:
与最长公共子序列挺相似的一道动态规划
      if(first[i] == second[j])
         f(i, j) = min(f(i-1, j-1)[nothing], f(i-1, j)+1[deletion], f(i, j-1)+1[insertion])
      else
         f(i, j) = min(f(i-1, j-1)+1[change], f(i-1, j)+1[deletion], f(i, j-1)+1[insertion])

其中,f(i, j)表示使first[0..i]转化成second[0..j]所需要的最小操作数

一个需要注意的问题是table的初始化,一开始想当然地将table[i][0], table[0][j]初始化为无穷大,导致出错
这可以说是一个普遍化的问题,就是要注意动态规划时的初始化

 1 int
 2 dfs()
 3 {
 4     int i, j;
 5     /* Attention: initialize */
 6     /* set table[i][0] and table[0][j] to INT_MAX, which caused WA */
 7     for(i=0; i<=m; i++)
 8         table[i][0= i;
 9     for(j=0; j<=n; j++)
10         table[0][j] = j;
11     for(i=1; i<=m; i++) {
12         for(j=1; j<=n; j++) {
13             if(x[i-1== y[j-1])
14                 table[i][j] = min(table[i-1][j-1], table[i-1][j]+1, table[i][j-1]+1);
15             else
16                 table[i][j] = min(table[i-1][j-1], table[i-1][j], table[i][j-1]) + 1;
17         }
18     }
19     return table[m][n];
20 }


posted @ 2010-06-29 22:39 simplyzhao 阅读(132) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1458

思路:
额...最长公共子序列(LCS), 经典动态规划(详情见CLRS)
     if(first[i] == second[j])
         f(i, j) = f(i-1, j-1) + 1
     else
         f(i, j) = max(f(i-1, j), f(i, j-1))

 1 int
 2 dp_lcs()
 3 {
 4     int i, j;
 5     for(i=0; i<=m; i++)
 6         table[i][0= 0;
 7     for(j=0; j<=n; j++)
 8         table[0][j] = 0;
 9 
10     for(i=1; i<=m; i++) {
11         for(j=1; j<=n; j++) {
12             if(fst[i-1== snd[j-1])
13                 table[i][j] = table[i-1][j-1+ 1;
14             else
15                 table[i][j] = max(table[i-1][j], table[i][j-1]);
16         }
17     }
18     return table[m][n];
19 }

posted @ 2010-06-29 22:06 simplyzhao 阅读(101) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=2479
http://acm.pku.edu.cn/JudgeOnline/problem?id=2593
http://acm.pku.edu.cn/JudgeOnline/problem?id=1050

思路:
基础: 最大子段和问题
给定N个整数(可能为负)组成的序列a1, a2, a3, ..., aN,求子段ai, a(i+1), ... , aj的和的最大值
非常典型的动态规划,状态迁移方程:
      f(i) = max(ai, f[i-1]+ai), f(i)表示以ai结尾的最大子段和
据此我们可以得到O(n)的求解算法

PKU 2479与2593这两题其实是同一个问题(买一送一),都是上述最大子段和问题的变形
一样非常自然的想法是枚举所有可能的"分开点", 然后分别计算前后两个子数组的最大子段和,不过如果依次枚举的话是会超时的
这时候就需要利用对于上述f(i)表达式的理解了, 我们可以依次从头到尾、从尾到头扫描两次原数组,并把相应的最大子段和分别保存起来,称为hd[i]和tl[i], 这里注意f(i)并非是最大子段和
假设现在枚举到分开点t, 那么a[0..t]的最大子段和可以通过hd[i]获得,a[t+1...len]的最大子段和则可以通过tl[i]获得
 1 /*
 2  * hd[i] stores the maximum sub-segment from arr[0..i]
 3  * tl[i] stores the maximum sub_segment from arr[i+1..n-1]
 4  */
 5 long *hd, *tl;
 6 
 7 long
 8 max_subsum(int *arr, long N)
 9 {
10     long i, temp, max;
11     /* hd */
12     hd[0= max = arr[0];
13     for(i=1; i<N; i++) {
14         temp = hd[i-1+ arr[i];
15         hd[i] = temp>arr[i] ? temp : arr[i];
16     }
17     for(i=1; i<N; i++) {
18         hd[i] = hd[i] > max ? hd[i] : max;
19         max = hd[i];
20     }
21     /* tl */
22     tl[N-1= max = arr[N-1];
23     for(i=N-2; i>=0; i--) {
24         temp = tl[i+1+ arr[i];
25         tl[i] = temp>arr[i] ? temp : arr[i];
26     }
27     for(i=N-2; i>=0; i--) {
28         tl[i] = tl[i] > max ? tl[i] : max;
29         max = tl[i];
30     }
31 }
32 
33 long
34 enumerate()
35 {
36     long i, temp, max = hd[0+ tl[1];
37     for(i=1; i<n-1; i++) {
38         temp = hd[i] + tl[i+1];
39         max = max>temp ? max : temp;
40     }
41     return max;
42 }

PKU 1050是一道"隐藏"地比较深的最大子段和问题,之所以说它隐藏的比较深,是因为题目要求的是求最大子矩阵问题
上网搜了别人的思路,才发现这题是可以转化成求最大子段和问题的:只要将矩阵的行或者列合并即可
不得不感叹这思路的精妙啊呵呵
 1 int
 2 max_subsum(int *arr, int N)
 3 {
 4     int i, t, max;
 5     max = t = arr[0];
 6     for(i=1; i<N; i++) {
 7         t = t+arr[i]>arr[i] ? t+arr[i] : arr[i];
 8         max = max>? max : t;
 9     }
10     return max;
11 }
12 
13 int 
14 enumerate()
15 {
16     int t, max = 0;
17     int i, j, k, len, temp[col];
18     memset(temp, 0sizeof(int)*col);
19     for(len=1; len<=row; len++) {
20         for(i=0; i<row; i++) {
21             for(j=i; j<len; j++) {
22                 for(k=0; k<col; k++) {
23                     temp[k] += arr[j][k];
24                 }
25             }
26             t = max_subsum(temp, col);
27             max = max>? max : t;
28             memset(temp, 0sizeof(int)*col);
29         }
30     }
31     return max;
32 }

posted @ 2010-06-29 16:48 simplyzhao 阅读(279) | 评论 (0)编辑 收藏
问题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1014

思路:
1. 深度搜索
看到该题的第一个想法就是DFS,很快就写出了代码:
 1 void
 2 dfs(int *values, int index, long cur_value_sum)
 3 {
 4     if(cur_value_sum >= value_half) {
 5         if(cur_value_sum == value_half)
 6             flag = 1;
 7         return;
 8     }
 9     if(index < 1)
10         return;
11     int i;
12     for(i=values[index]; i>=0 && (!flag); i--) {
13         cur_value_sum += (i*index);
14         dfs(values, index-1, cur_value_sum);
15         cur_value_sum -= (i*index);
16     }
17 }
额...结果TLE...
仔细看题,发现maximum total number will be 20000, 所以超时几乎是肯定的
其实,discuss里有人用DFS+Cut通过的,只是自己太菜,还不会减枝

2. 动态规划
2.1 from: 
http://www.cppblog.com/AClayton/archive/2007/09/14/32185.html
声明数组can_reach[60005]
can_reach[i]=1, 表示存在一个人获得价值为 i ,另一个人获得价值为value_sum-i的方案
我们的目标就是要确定can_reach[value_sum>>1]是否等于1
 1 int
 2 dp()
 3 {
 4     int i, j, k, temp, cur_max = 0;
 5     can_reach[0= 1;
 6     for(i=1; i<=VALUE_MAX; i++) {
 7         if(num[i] > 0) {
 8             for(j=cur_max; j>=0; j--) { /* tricky, pay attention */
 9                 if(can_reach[j]) {
10                     for(k=1; k<=num[i]; k++) {
11                         temp = i*k+j;
12                         if(temp == value_half)
13                             return 1;
14                         if(temp>value_half) /*  initial: if(temp>value_half || can_reach[temp]) break; */
15                             break;
16                         can_reach[temp] = 1;
17                     }
18                 }
19             }
20         }
21         cur_max += (i*num[i]);
22         if(cur_max>value_half)
23             cur_max = value_half;
24     }
25 }
注意: 上述代码的第14行,原文中存在减枝,但没有看懂,所以没有放进去,还好,该代码还是AC了

2.2  from: http://blog.sina.com.cn/s/blog_5c95cb070100cvt8.html
该问题可以转化成一个0-1背包模型(见:背包九讲)
关键是下面的数论知识:
对于任意一个正整数 n, 它可以表示成:
      n = 1 + 1<<1 + 1<<2 + ... + 1<<k + res[余数]
我们可以得到:对于1<=i<=n的任意正整数 i, 它肯定可以表示成: 1, 1<<1, 1<<2, ... 1<<k, res的一个组合
因此,对于该题,我们可以对输入做预处理:
 1 len = 0;
 2 memset(value_weight, 0sizeof(value_weight));
 3 for(i=1; i<=VALUE; i++) {
 4     if(num[i] != 0) {
 5          j = 0;
 6          while((1<<(j+1)) <= (num[i]+1)) {
 7                 value_weight[len++= i*(1<<j);
 8                 ++j;
 9          }
10         if((num[i]+1-(1<<j))>0)
11         value_weight[len++= i*(num[i]+1-(1<<j));
12    }
13 }
接下来,原问题就转化成:
背包容量value_half(value_sum/2),每件物品的cost与value都是value_weight[i],考虑是否存在一种方案,使得总价值等于value_half
0-1背包的典型DP状态方程:
      f[i][j] = max(f[i-1][j], f[i-1][j-c[i]]+v[i]), f[i][j]表示前i件物品放入容量为j的背包而可以获得的最大价值
 1 int
 2 knapsack()
 3 {
 4     int i, w, pre, cur;
 5     memset(table, 0sizeof(table));
 6     for(w=value_weight[0]; w<=value_half; w++) {
 7         table[0][w] = value_weight[0];
 8         if(table[0][w] == value_half)
 9             return 1;
10     }
11     for(i=1; i<len; i++) {
12         cur = i%2;
13         pre = (i+1)%2;
14         for(w=0; w<=value_half; w++) {
15             if(w>=value_weight[i])
16                 table[cur][w] = max(table[pre][w], table[pre][w-value_weight[i]]+value_weight[i]);
17             else
18                 table[cur][w] = table[pre][w];
19             if(table[cur][w] == value_half)
20                 return 1;
21         }
22     }
23     return 0;
24 }
AC [ Memory: 836K, Time: 16MS]
posted @ 2010-06-28 23:30 simplyzhao 阅读(184) | 评论 (0)编辑 收藏

算法,始终是自己最为薄弱的环节。
距离正式开始找实习、找工作还有一年的时间,好好加油。
坚信:勤能补拙

训练方式:PKU
目标:不求达到ACM的程度(这个...当然可能性也不大(*^__^*) 嘻嘻……), 但求熟练掌握基础

代码:
# Non-members may check out a read-only working copy anonymously over HTTP.
svn checkout http:
//code-pearl.googlecode.com/svn/trunk/ code-pearl-read-only
posted @ 2010-06-28 20:00 simplyzhao 阅读(91) | 评论 (0)编辑 收藏
仅列出标题
共21页: First 13 14 15 16 17 18 19 20 21 

导航

<2011年6月>
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

统计

常用链接

留言簿(1)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜