挑战程序设计竞赛(第2版)的目录

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了挑战程序设计竞赛(第2版)的目录相关的知识,希望对你有一定的参考价值。

参考技术A

《挑战程序设计竞赛(第2版)》
第1章 蓄势待发——准备篇 1   1.1  何谓程序设计竞赛 2   1.2  最负盛名的程序设计竞赛 5   1.2.1  世界规模的大赛——google code jam(gcj) 5   1.2.2  向高排名看齐!——topcoder 5   1.2.3  历史最悠久的竞赛—— acm-icpc 6   1.2.4  面向中学生的信息学奥林匹克竞赛——joi-ioi 6   1.2.5  通过网络自动评测——online judge(oj) 6   1.3  本书的使用方法 7   1.3.1  本书所涉及的内容 7   1.3.2  所用的编程语言 7   1.3.3  题目描述的处理 7   1.3.4  程序结构 7   1.3.5  练习题 8   1.3.6  读透本书后更上一层楼的练习方法 8   1.4  如何提交解答 9   1.4.1  poj的提交方法 9   1.4.2  gcj的提交方法 11   1.5  以高效的算法为目标 15
.1.5.1  什么是复杂度 15   1.5.2  关于运行时间 15   1.6  轻松热身 16   1.6.1  先从简单题开始 16   1.6.2  poj的题目ants 18   1.6.3  难度增加的抽签问题 20   第2章 初出茅庐——初级篇 25   2.1  最基础的“穷竭搜索” 26   2.1.1  递归函数 26   2.1.2  栈 27   2.1.3  队列 28   2.1.4  深度优先搜索 29   2.1.5  宽度优先搜索 33   2.1.6  特殊状态的枚举 37   2.1.7  剪枝 38   2.2  一往直前!贪心法 39   2.2.1  硬币问题 39   2.2.2  区间问题 40   2.2.3  字典序最小问题 43   2.2.4  其他例题 45   2.3  记录结果再利用的“动态规划” 51   2.3.1  记忆化搜索与动态规划 51   2.3.2  进一步探讨递推关系 57   2.3.3  有关计数问题的dp 66   2.4  加工并存储数据的数据结构 70   2.4.1  树和二叉树 70   2.4.2  优先队列和堆 71   2.4.3  二叉搜索树 77   2.4.4  并查集 84   2.5  它们其实都是“图” 91   2.5.1  图是什么 91   2.5.2  图的表示 94   2.5.3  图的搜索 97   2.5.4  最短路问题 99   2.5.5  最小生成树 105   2.5.6  应用问题 107   2.6  数学问题的解题窍门 113   2.6.1  辗转相除法 113   2.6.2  有关素数的基础算法 117   2.6.3  模运算 121   2.6.4  快速幂运算 122   2.7  一起来挑战gcj的题目(1) 125   2.7.1  minimum scalar product 125   2.7.2  crazy rows 127   2.7.3  bribe the prisoners 129   2.7.4  millionaire 132   第3章 出类拔萃——中级篇 137   3.1  不光是查找值!“二分搜索” 138   3.1.1  从有序数组中查找某个值 138   3.1.2  假定一个解并判断是否可行 140   3.1.3  最大化最小值 142   3.1.4  最大化平均值 143   3.2  常用技巧精选(一) 146   3.2.1  尺取法 146   3.2.2  反转(开关问题) 150   3.2.3  弹性碰撞 158   3.2.4  折半枚举(双向搜索) 160   3.2.5  坐标离散化 164   3.3  活用各种数据结构 167   3.3.1  线段树 167   3.3.2  binary indexed tree 174   3.3.3  分桶法和平方分割 183   3.4  熟练掌握动态规划 191   3.4.1  状态压缩dp 191   3.4.2  矩阵的幂 199   3.4.3  利用数据结构高效求解 206   3.5  借助水流解决问题的网络流 209   3.5.1  最大流 209   3.5.2  最小割 212   3.5.3  二分图匹配 217   3.5.4  一般图匹配 220   3.5.5  匹配、边覆盖、独立集和顶点覆盖 221   3.5.6  最小费用流 222   3.5.7  应用问题 228   3.6  与平面和空间打交道的计算几何 250   3.6.1  计算几何基础 250   3.6.2  极限情况 255   3.6.3  平面扫描 258   3.6.4  凸包 260   3.6.5  数值积分 263   3.7  一起来挑战gcj的题目(2) 267   3.7.1  numbers 267   3.7.2  no cheating 269   3.7.3  stock charts 271   3.7.4  watering plants 273   3.7.5  number sets 278   3.7.6  wi-fi towers 280   第4章 登峰造极——高级篇 285   4.1  更加复杂的数学问题 286   4.1.1  矩阵 286   4.1.2  模运算的世界 291   4.1.3  计数 295   4.1.4  具有对称性的计数 300   4.2  找出游戏的必胜策略 305   4.2.1  游戏与必胜策略 305   4.2.2  nim 311   4.2.3  grundy数 315   4.3  成为图论大师之路 320   4.3.1  强连通分量分解 320   4.3.2  2-sat 324   4.3.3  lca 328   4.4  常用技巧精选(二) 335   4.4.1  栈的运用 335   4.4.2  双端队列的运用 337   4.4.3  倍增法 345   4.5  开动脑筋智慧搜索 350   4.5.1  剪枝 350   4.5.2  a*与ida* 356   4.6  划分、解决、合并:分治法 359   4.6.1  数列上的分治法 359   4.6.2  树上的分治法 360   4.6.3  平面上的分治法 364   4.7  华丽地处理字符串 368   4.7.1  字符串上的动态规划算法 368   4.7.2  字符串匹配 373   4.7.3  后缀数组 378   4.8  一起来挑战gcj的题目(3) 387   4.8.1  mine layer 387   4.8.2  year of more code jam 392   4.8.3  football team 395   4.8.4  endless knight 399   4.8.5  the year of code jam 403   本书中未涉及的拓展主题 408   书中例题列表 411   参考文献 413

《挑战程序设计竞赛》课后练习题解集——3.4 熟练掌握动态规划

《挑战程序设计竞赛》课后练习题解集——3.4 熟练掌握动态规划

状态压缩DP

POJ 2441  有N头牛,M个槽,N,M≤20,每头牛只在指定的pi个槽里进食,不与其他牛共享槽。问有多少种分配方案。

  dp[i][S],当前第i头牛要进食,槽的使用状态为S

技术图片
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <vector>
 4 using namespace std;
 5 
 6 int n, m;
 7 int dp[1 << 21];
 8 vector<int> cow[21];
 9 
10 int next(int k) {
11     int x = k & (-k), y = x + k;
12     return k = ((k & (~y)) / x >> 1) | y;
13 }
14 
15 int main() {
16     int p, x;
17     scanf("%d%d", &n, &m);
18     for (int i = 0; i < n; i++) {
19         scanf("%d", &p);
20         for (int j = 0; j < p; j++) {
21             scanf("%d", &x);
22             x--;
23             cow[i].push_back(x);
24         }
25     }
26     dp[0] = 1;
27     for (int i = 0; i < n; i++) {
28         for (int j = (1 << i + 1) - 1; j < 1 << m; j = next(j)) {
29             for (int k = 0; k < cow[i].size(); k++) {
30                 if (j >> cow[i][k] & 1) {
31                     dp[j] += dp[j & ~(1 << cow[i][k])];
32                 }
33             }
34         }
35     }
36     int res = 0;
37     for (int i = (1 << n) - 1; i < 1 << m; i = next(i)) {
38         res += dp[i];
39     }
40     printf("%d", res);
41 }
View Code

 

POJ 3254  M×N的农场,N, M≤12,农夫要选择若干个肥沃的地方种种子,这些种子不能相邻,问有多少种方案。

  和书上的铺砖问题做法类似,但是要注意此时used数组的含义不是已有种子,而是(i-1, j)有种子,所以(i, j+1)used为true时,(i, j)仍然可以播种;另外初始化的时候,可以全为1或者有意义的情况为1,因为对于(m-1, n-1) dp数组是考虑最后一行的放法,原先的铺砖问题砖一定不会铺到下一行,所有初始化只需dp[0]=1。

技术图片
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 using namespace std;
 5 const int mod = 1e8;
 6 
 7 int m, n, cnt, a[13][13];
 8 int dp[2][(1 << 12) + 5];
 9 
10 int main() {
11     scanf("%d %d", &m, &n);
12     for (int i = 0; i < m; i++) {
13         for (int j = 0; j < n; j++) scanf("%d", &a[i][j]);
14     }
15     int *crt = dp[0], *next = dp[1];
16 
17     /*  int last = 0;
18       for (int i = n - 1; i >= 0; i--) {
19           last <<= 1;
20           last += a[m - 1][i] == 0;
21       }*/
22     for (int i = 0; i < 1 << n; i++) {
23         /* if (!(last & i)) */ crt[i] = 1;
24     }
25 
26     for (int i = m - 1; i >= 0; i--) {
27         for (int j = n - 1; j >= 0; j--) {
28             for (int s = 0; s < 1 << n; s++) {
29                 if (a[i][j] == 0 || s >> j & 1)
30                     next[s] = crt[s & ~(1 << j)];
31                 else {
32                     int t = s | 1 << j;
33                     if (j < n - 1) t |= 1 << j + 1;
34                     next[s] = (crt[s] + crt[t]) % mod;
35                 }
36             }
37             swap(crt, next);
38         }
39     }
40     printf("%d
", crt[0]);
41 }
View Code

另外,还可以预处理不相邻的种子的放法,按行DP,枚举上下两行的情况来转移

技术图片
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 using namespace std;
 5 const int mod = 1e8;
 6 
 7 int m, n, cnt, a[13][13], bad[13];
 8 int sta[400], dp[2][400];
 9 
10 int main() {
11     scanf("%d%d", &m, &n);
12     for (int i = 0; i < m; i++) {
13         for (int j = 0; j < n; j++) scanf("%d", &a[i][j]);
14         for (int j = n - 1; j >= 0; j--) {
15             bad[i] <<= 1;
16             bad[i] += !a[i][j];
17         }
18     }
19     for (int i = 0; i < 1 << n; i++) {
20         if (!((i << 1) & i)) sta[cnt++] = i;
21     }
22     int *crt = dp[0], *next = dp[1];
23     for (int i = 0; i < cnt; i++) {
24         if (!(sta[i] & bad[0])) crt[i] = 1;
25     }
26     for (int i = 1; i < m; i++) {
27         for (int j = 0; j < cnt; j++) {  //当前行的放置方式
28             if (sta[j] & bad[i]) continue;
29             for (int k = 0; k < cnt; k++) {  //上一行的放置方式
30                 if (sta[j] & sta[k]) continue;
31                 next[j] = (next[j] + crt[k]) % mod;
32             }
33         }
34         swap(crt, next);
35         for (int i = 0; i < cnt; i++) next[i] = 0;
36     }
37     int res = 0;
38     for (int i = 0; i < cnt; i++) res = (res + crt[i]) % mod;
39     printf("%d", res);
40 }
View Code

 

POJ 2836  平面上有N(≤15)个点,要用若干个矩形覆盖这些点,每个矩形至少覆盖到两个点且面积大于0,问这些矩形的面积总和的最小值。

  任何方案都可以分解成若干个这样的矩形,它的对角一定有2个点。枚举N^2这样的矩形作为转移边,以覆盖哪些点为状态,就可以转移了

技术图片
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 using namespace std;
 5 const int INF = 0x7f7f7f7f;
 6 
 7 int n, cnt, x[16], y[16];
 8 int area[270], cover[270], dp[1 << 16];
 9 
10 int main() {
11     while (~scanf("%d", &n) && n) {
12         for (int i = 0; i < n; i++) {
13             scanf("%d", &x[i]);
14             scanf("%d", &y[i]);
15         }
16         cnt = 0;
17         memset(cover, 0, sizeof(cover));
18         memset(dp, 127, sizeof(dp));
19         for (int i = 0; i < n; i++) {
20             for (int j = i + 1; j < n; j++) {
21                 int x1 = min(x[i], x[j]), x2 = max(x[i], x[j]),
22                     y1 = min(y[i], y[j]), y2 = max(y[i], y[j]);
23                 area[cnt] = (x1 == x2 ? 1 : x2 - x1) * (y1 == y2 ? 1 : y2 - y1);
24                 for (int k = 0; k < n; k++) {
25                     if (x1 <= x[k] && x[k] <= x2 && y1 <= y[k] && y[k] <= y2) {
26                         cover[cnt] += (1 << k);
27                     }
28                 }
29                 cnt++;
30             }
31         }
32         dp[0] = 0;
33         for (int i = 0; i < 1 << n; i++) {
34             if (dp[i] != INF) {
35                 for (int j = 0; j < cnt; j++) {
36                     dp[i | cover[j]] = min(dp[i | cover[j]], dp[i] + area[j]);
37                 }
38             }
39         }
40         printf("%d
", dp[(1 << n) - 1]);
41     }
42 }
View Code

 

POJ 1795  给出N个串Si, N≤15, Si≤100,问最短且字典序最小的主串,使得这N个串都是它的子串。

  预处理N个串间互为字串的情况,剩下要决定的就是连接顺序。dp[i][S],代表包含的串集合为S,开头是串i,之所以以开头而不是结尾为是为了后面要求字典序最小方便。(我自己的这个版本代码写麻烦了,dp的时候就储存串的连接顺序要好些;而且在以长度回溯串时,如果没有预处理起始串互为字串,只比较前缀其实是错的)

技术图片
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 using namespace std;
 5 const int INF = 0x7f7f7f7f;
 6 
 7 int n, cnt, sta[16];
 8 string str[16];
 9 int cost[16][16], dp[16][(1 << 15) + 5];
10 
11 int main() {
12     int T;
13     scanf("%d", &T);
14     for (int I = 1; I <= T; I++) {
15         memset(sta, 0, sizeof(sta));
16         memset(cost, 0, sizeof(cost));
17         memset(dp, 127, sizeof(dp));
18         cnt = 0;
19         scanf("%d", &n);
20         for (int i = 0; i < n; i++) cin >> str[i];
21         for (int i = 0; i < n; i++) {
22             for (int j = i + 1; j < n; j++) {
23                 if (str[i].find(str[j]) != string::npos)
24                     sta[j] = 1;
25                 else if (str[j].find(sta[i]) != string::npos)
26                     sta[i] = 1;
27             }
28         }
29         for (int i = 0; i < n; i++)
30             if (!sta[i]) str[cnt++] = str[i];
31         for (int i = 0; i < cnt; i++) {
32             for (int j = 0; j < cnt; j++) {
33                 if (i != j) {
34                     int len = min(str[i].size(), str[j].size());
35                     for (int k = 0; k <= len; k++) {
36                         if (str[i].substr(str[i].size() - k) ==
37                             str[j].substr(0, k)) {
38                             cost[i][j] = str[i].size() - k;
39                             //在j前面接i要花cost[i][j]的代价
40                         }
41                     }
42                 }
43             }
44         }
45         for (int i = 0; i < cnt; i++) dp[i][1 << i] = str[i].size();
46         for (int i = 1; i < 1 << cnt; i++) {
47             for (int j = 0; j < cnt; j++) {
48                 if (dp[j][i] != INF) {
49                     for (int k = 0; k < cnt; k++) {
50                         if (!(i >> k & 1)) {
51                             dp[k][i | (1 << k)] =
52                                 min(dp[k][i | (1 << k)], cost[k][j] + dp[j][i]);
53                         }
54                     }
55                 }
56             }
57         }
58         int id = 0, sz = (1 << cnt) - 1;
59         for (int i = 1; i < cnt; i++) {
60             if (dp[i][sz] < dp[id][sz] ||
61                 dp[i][sz] == dp[id][sz] && str[i] < str[id]) {
62                 id = i;
63             }
64         }
65         string ans = str[id];
66         int S = sz;
67         for (int i = 1; i < cnt; i++) {
68             int next_id = -1;
69             string tmp = "";
70             for (int j = 0; j < cnt; j++) {
71                 if (id != j && (S >> j & 1) &&
72                     dp[id][S] == dp[j][S & ~(1 << id)] + cost[id][j]) {
73                     string t = str[j].substr(str[id].size() - cost[id][j]);
74                     if (next_id == -1 || t < tmp) {
75                         tmp = t;
76                         next_id = j;
77                     }
78                 }
79             }
80             ans += tmp;
81             S &= ~(1 << id);
82             id = next_id;
83         }
84         printf("Scenario #%d:
", I);
85         cout << ans << "

";
86     }
87 }
View Code

 

POJ 3411  给出N个点,M条边,N, M≤10,每条边有ai, bi, ci, Pi, Ri,Pi≤Ri,表示从点ai到bi时,若已经过ci,代价为Pi,否则为Ri。问从点1到点N的最小代价

  和旅行商问题类似,dp[i][S]表示当前经过的点集合为S,在点 i 的最小代价。但是此时状态之间不是拓扑序,可能出现回到已经经过过的点的情况,所以要用最段路算法。

技术图片
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <queue>
 5 #include <vector>
 6 using namespace std;
 7 #define P pair<int, int>
 8 const int INF = 0x7f7f7f7f;
 9 
10 int n, m;
11 int dp[11][(1 << 10) + 5];
12 
13 struct edge {
14     int to, c, p, r;
15 };
16 vector<edge> G[11];
17 
18 struct node {
19     int cost, u, S;
20     bool operator<(const node& o) const { return cost < o.cost; }
21 };
22 priority_queue<node> que;
23 
24 int main() {
25     int a, b, c, p, r;
26     scanf("%d %d", &n, &m);
27     for (int i = 0; i < m; i++) {
28         scanf("%d %d %d %d %d", &a, &b, &c, &p, &r);
29         a--, b--, c--;
30         edge x = {b, c, p, r};
31         G[a].push_back(x);
32     }
33     memset(dp, 127, sizeof(dp));
34     dp[0][1] = 0;
35     node o = {0, 0, 1};
36     que.push(o);
37     while (!que.empty()) {
38         node p = que.top();
39         que.pop();
40         int u = p.u, rou = p.S;
41         for (int i = 0; i < G[u].size(); i++) {
42             edge es = G[u][i];
43             int v = es.to, cost;
44             if (rou >> es.c & 1)
45                 cost = es.p;
46             else
47                 cost = es.r;
48             if (dp[v][rou | (1 << v)] > dp[u][rou] + cost) {
49                 dp[v][rou | (1 << v)] = dp[u][rou] + cost;
50                 o = {dp[v][rou | (1 << v)], v, rou | 1 << v};
51                 que.push(o);
52             }
53         }
54     }
55     int res = INF;
56     for (int i = 0; i < 1 << n; i++) res = min(res, dp[n - 1][i]);
57     if (res != INF)
58         printf("%d
", res);
59     else
60         printf("impossible
");
61 }
View Code

另外不知道为什么用队列代替优先队列也能0ms过。dij用队列代替优先队列的算法复杂度有待分析

技术图片
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <queue>
 5 #include <vector>
 6 using namespace std;
 7 #define P pair<int, int>
 8 const int INF = 0x7f7f7f7f;
 9 
10 int n, m;
11 int dp[11][(1 << 10) + 5];
12 
13 struct edge {
14     int to, c, p, r;
15 };
16 vector<edge> G[11];
17 
18 queue<P> que;
19 
20 int main() {
21     int a, b, c, p, r;
22     scanf("%d %d", &n, &m);
23     for (int i = 0; i < m; i++) {
24         scanf("%d %d %d %d %d", &a, &b, &c, &p, &r);
25         a--, b--, c--;
26         edge x = {b, c, p, r};
27         G[a].push_back(x);
28     }
29     memset(dp, 127, sizeof(dp));
30     dp[0][1] = 0;
31     que.push(P(0, 1));
32     while (!que.empty()) {
33         P p = que.front();
34         que.pop();
35         int u = p.first, rou = p.second;
36         for (int i = 0; i < G[u].size(); i++) {
37             edge es = G[u][i];
38             int v = es.to, cost;
39             if (rou >> es.c & 1)
40                 cost = es.p;
41             else
42                 cost = es.r;
43             if (dp[v][rou | (1 << v)] > dp[u][rou] + cost) {
44                 dp[v][rou | (1 << v)] = dp[u][rou] + cost;
45                 que.push(P(v, rou | (1 << v)));
46             }
47         }
48     }
49     int res = INF;
50     for (int i = 0; i < 1 << n; i++) res = min(res, dp[n - 1][i]);
51     if (res != INF)
52         printf("%d
", res);
53     else
54         printf("impossible
");
55 }
View Code

 

 矩阵的幂

POJ 3420  问用1×2的矩阵填充4×N的矩阵有多少种方法。

  设题述答案为An。考虑这样一类的填充方案,它在前N-1列的空隙中,都至少有一个横放的矩形穿过了这个空隙,设这样的方案有Bn种。我们有B1=1, B2 = 4, Bi = 2 (i为大于2的奇数),Bi = 3(i为大于2的偶数)。对于一般的填充方案,考虑从左到右(右到左是一样)第一列不满足有横放的矩阵穿过空隙的情况(也可能不存在,属于Bi的范畴),然后以此为界限就可以把整个矩形视作两部分,让左右方案数相乘。得到递推关系 An = B1 × An-1 + B2 × An-2 + ... + Bn = An-1 + 4An-2 + 2An-3 + 3An-4 + ...。我们发现后面系数是以“2 3 2 3”循环的,利用这个可以消去后面的许多项,最后得到An = An-1 + 5×An-2 + An-3 - An-4。

技术图片
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <vector>
 4 using namespace std;
 5 #define ll long long
 6 typedef vector<long long> vec;
 7 typedef vector<vec> mat;
 8 
 9 int n, m;
10 
11 mat mul(mat A, mat B) {
12     mat C(4, vec(4, 0));
13     for (int i = 0; i < 4; i++) {
14         for (int j = 0; j < 4; j++) {
15             for (int k = 0; k < 4; k++) {
16                 C[i][j] = ((C[i][j] + A[i][k] * B[k][j]) % m + m) % m;
17             }
18         }
19     }
20     return C;
21 }
22 
23 mat ksm(mat A, int x) {
24     mat B(4, vec(4, 0));
25     for (int i = 0; i < 4; i++) B[i][i] = 1;
26     while (x) {
27         if (x & 1) B = mul(B, A);
28         A = mul(A, A);
29         x >>= 1;
30     }
31     return B;
32 }
33 
34 void solve() {
35     mat A(4, vec(4));
36     A[0][0] = 1, A[0][1] = 5, A[0][2] = 1, A[0][3] = -1;
37     A[1][0] = A[2][1] = A[3][2] = 1;
38     A = ksm(A, n - 1);
39     printf("%lld
", (A[3][0] * 36 + A[3][1] * 11 + A[3][2] * 5 + A[3][3]) % m);
40 }
41 
42 int main() {
43     while (~scanf("%d%d", &n, &m), n && m) {
44         solve();
45     }
46 }
View Code

 

 POJ 3735  有n只小猫,要依次执行k个操作并重复m次,n≤100, k≤100, ≤1e9,操作分别有:1. 使第i只小猫获得一个坚果;2. 使第i只小猫吃掉它的所有坚果;3. 交换第i只小猫与第j只小猫的坚果。问最后每只小猫拥有坚果的情况。

  小猫的坚果数和常数1写作列向量,3种操作都对应到矩阵运算即可。这题的矩阵相乘需要判0优化,否则会T。

技术图片
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <vector>
 4 using namespace std;
 5 #define ll long long
 6 typedef vector<ll> vec;
 7 typedef vector<vec> mat;
 8 
 9 int n, m, k;
10 
11 mat mul(mat &A, mat &B) {
12     mat C(A.size(), vec(B[0].size()));
13     for (int i = 0; i < A.size(); i++) {
14         for (int j = 0; j < A[0].size(); j++) {
15             if (A[i][j])
16                 for (int k = 0; k < B[0].size(); k++) {
17                     C[i][k] += A[i][j] * B[j][k];
18                 }
19         }
20     }
21     return C;
22 }
23 
24 mat ksm(mat A, int x) {
25     mat B(A.size(), vec(A.size()));
26     for (int i = 0; i < A.size(); i++) B[i][i] = 1;
27     while (x) {
28         if (x & 1) B = mul(B, A);
29         A = mul(A, A);
30         x >>= 1;
31     }
32     return B;
33 }
34 
35 int main() {
36     char op;
37     int x, y;
38     while (~scanf("%d%d%d", &n, &m, &k), n || m || k) {
39         mat a(n + 1, vec(n + 1));
40         for (int i = 0; i < n + 1; i++) a[i][i] = 1;
41         for (int i = 0; i < k; i++) {
42             getchar();
43             scanf("%c", &op);
44             if (op == g) {
45                 scanf("%d", &x);
46                 for (int i = 0; i < n + 1; i++) a[x - 1][i] += a[n][i];
47             } else if (op == e) {
48                 scanf("%d", &x);
49                 for (int i = 0; i < n + 1; i++) a[x - 1][i] = 0;
50             } else {
51                 scanf("%d%d", &x, &y);
52                 for (int i = 0; i < n + 1; i++) swap(a[x - 1][i], a[y - 1][i]);
53             }
54         }
55         a = ksm(a, m);
56         for (int i = 0; i < n; i++)
57             printf("%lld%c", a[i][n], i == n - 1 ? 
 :  );
58     }
59 }
View Code

 

 利用数据结构高效求解

 POJ 3171  有N只牛,每只牛可以在[T1, T2]的时间里工作,需要支付它S薪水。现需要[M, E]的时间区间里每一时刻都有牛在工作,问最少支付的薪水是多少

  dp[i]表示到第i时刻都有牛工作最少需要支付的薪水是多少,线段树维护。

技术图片
 1 #include <algorithm>
 2 #include <cstdio>
 3 #include <iostream>
 4 using namespace std;
 5 #define ll long long
 6 const int N = 1e4 + 5;
 7 const int M = 86399 + 5;
 8 const ll INF = 1e10;
 9 
10 int n, m, e;
11 
12 ll min(ll a, ll b) { return a < b ? a : b; }
13 
14 struct sec {
15     int l, r, s;
16 } a[N];
17 
18 bool operator<(sec x, sec y) { return x.r < y.r; }
19 
20 struct node {
21     int l, r, f;
22     ll Min;
23 } tree[M * 4];
24 
25 void build(int k, int lch, int rch) {
26     tree[k] = {lch, rch, 0, INF};
27     if (lch == rch) return;
28     int mid = (lch + rch) / 2;
29     build(k * 2, lch, mid);
30     build(k * 2 + 1, mid + 1, rch);
31 }
32 
33 void down(int k) {
34     if (tree[k * 2].Min > tree[k].Min) {
35         tree[k * 2].Min = tree[k].Min;
36         tree[k * 2].f = 1;
37     }
38     if (tree[k * 2 + 1].Min > tree[k].Min) {
39         tree[k * 2 + 1].Min = tree[k].Min;
40         tree[k * 2 + 1].f = 1;
41     }
42     tree[k].f = 0;
43 }
44 
45 ll ask(int k, int x) {
46     if (tree[k].l == tree[k].r) return tree[k].Min;
47     if (tree[k].f) down(k);
48     int mid = (tree[k].l + tree[k].r) / 2;
49     if (x <= mid) return ask(k * 2, x);
50     return ask(k * 2 + 1, x);
51 }
52 
53 void update(int k, int lch, int rch, ll num) {
54     if (lch <= tree[k].l && tree[k].r <= rch) {
55         tree[k].Min = min(tree[k].Min, num);
56         tree[k].f = 1;
57         return;
58     }
59     if (tree[k].f) down(k);
60     int mid = (tree[k].l + tree[k].r) / 2;
61     if (lch <= mid) update(k * 2, lch, rch, num);
62     if (rch > mid) update(k * 2 + 1, lch, rch, num);
63 }
64 
65 int main() {
66     scanf("%d %d %d", &n, &m, &e);
67     e -= (m - 1);
68     for (int i = 0; i < n; i++) {
69         scanf("%d %d %d", &a[i].l, &a[i].r, &a[i].s);
70         a[i].l -= (m - 1);
71         a[i].r -= (m - 1);
72     }
73     sort(a, a + n);
74     build(1, 1, e);
75     for (int i = 0; i < n; i++) {
76         if (a[i].l == 1) {
77             update(1, a[i].l, a[i].r, a[i].s);
78         } else {
79             update(1, a[i].l, a[i].r, ask(1, a[i].l - 1) + a[i].s);
80         }
81     }
82     ll res = ask(1, e);
83     if (res == INF)
84         printf("-1
");
85     else
86         printf("%lld
", res);
87 }
View Code

 

END

以上是关于挑战程序设计竞赛(第2版)的目录的主要内容,如果未能解决你的问题,请参考以下文章

挑战程序设计竞赛(第2版)第112页勘误

挑战程序设计竞赛 PDF下载

第1章蓄势待发准备篇

算法竞赛入门经典第2版 第1章

《挑战程序设计竞赛》课后练习题解集——3.4 熟练掌握动态规划

面试高级算法梳理笔记