题目链接:2010 ACM-ICPC Multi-University Training Contest(1)——Host by FZU
昨天superbin把这套题拿出来做练习,5个小时过去了,杯具就这样发生了,一道都没过!
比赛后,看了wzc大牛的解题报告,刷了三道
hdu3433: A Task Process
DP + 二分枚举
k表示第i个工人完成k件甲任务,(T - Aik) / Bi 表示剩余的时间用来完成的乙任务数。
平时dp学的太龊了,改了好久才完成。dp的时候对dp数组的初始化是个很关键的问题,有时候即使转移方程写出来了,也不一定能AC,这一题,可以将不可能完成的任务都赋成-1,dp数组初始化为-1.
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
2 #include < string .h >
3 #define NN 52
4 #define XX 202
5 #define INF 1 << 30
6
7 int X, N, Y;
8 int dp[NN][XX];
9 int cost[NN][ 2 ];
10 int Min( int a, int b){
11 return a < b ? a : b;
12 }
13
14 int Max( int a, int b){
15 return a > b ? a : b;
16 }
17
18 int Dp( int T){
19 int i, j, k, t;
20
21 for (i = 0 ; i <= N; i ++ ){ // 数组初始化
22 for (j = 0 ; j <= X; j ++ ){
23 dp[i][j] = - 1 ;
24 }
25 }
26
27 for (i = 0 ; i <= X; i ++ ){
28 t = T - i * cost[ 1 ][ 0 ];
29 if (t < 0 )
30 dp[ 1 ][i] = - 1 ; // 如果剩余的时间小于0,表示不可能完成,这里开始置成0了
31 else dp[ 1 ][i] = t / cost[ 1 ][ 1 ];
32 }
33
34 for (i = 2 ; i <= N; i ++ ){
35 for (j = 0 ; j <= X; j ++ ){
36 for (k = 0 ; k <= j; k ++ ){
37 if (dp[i - 1 ][k] < 0 ) break ; // 如果不可能,当k更大的时候就更不可能了
38 if (T - (j - k) * cost[i][ 0 ] >= 0 ) // 剩余时间的得保证非负
39 dp[i][j] = Max(dp[i][j], dp[i - 1 ][k] + (T - (j - k) * cost[i][ 0 ]) / cost[i][ 1 ]);
40 }
41 }
42 }
43 return dp[N][X] >= Y;
44 }
45 int main()
46 {
47 int T, mina, minb, mid, low, hig, ans, maxT, i;
48 int it = 1 ;
49 scanf( " %d " , & T);
50 while (T -- ){
51 scanf( " %d%d%d " , & N, & X, & Y);
52 mina = minb = INF;
53 for (i = 1 ; i <= N; i ++ ){
54 scanf( " %d%d " , & cost[i][ 0 ], & cost[i][ 1 ]);
55 mina = Min(mina, cost[i][ 0 ]);
56 minb = Min(minb, cost[i][ 1 ]);
57 }
58 maxT = mina * X + minb * Y; // 取一个时间上限
59 low = 0 ;
60 hig = maxT;
61 ans = hig;
62 do { // 二分时间,来判断是否可行,枚举到最短时间
63 mid = (low + hig) >> 1 ;
64 if (Dp(mid)){
65 ans = mid;
66 hig = mid - 1 ;
67 } else {
68 low = mid + 1 ;
69 }
70 } while (low <= hig);
71 printf( " Case %d: %d\n " , it ++ , ans);
72 }
73 return 0 ;
74 }
75
跑的比较慢,不过还是过了,应该还有更好的方法吧。
hdu3440: House Man
差分约束系统 + SPFA
比较简单,不过比赛的时候一直WA,没想到这题居然卡INF,ans的最大值为10^9,我开始定义INF 为0xffffff,正好差了一点,就杯具了。
2 int h, id;
3 }NODE1;
4 NODE1 f[NN];
h为高度,id为house左右次序编号
S为最矮的house的编号,T为最高的house的编号
按高度排序。一共有两种边:
1. 位置相邻的两个house间距 >= 1,<id, id - 1, -1>(2=< id <= N)
2. 高度相邻的两个house间距 <= 4,<min(f[i-1].id, f[i].id), max(f[i-1].id, f[i].id), D>(2 <= i <= N)
处理的时候令dis[S < T ? S : T] = 0
输出的结果是dis[S < T ? T : S]
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
2 #include < stdlib.h >
3 #include < string .h >
4 #define NN 1004
5 #define MM 2004
6 #define INF 0x3fffffff // 开始的时候定义小了,杯具
7 typedef struct node1{
8 int h, id;
9 }NODE1;
10 NODE1 f[NN];
11 typedef struct node{
12 int v, wt;
13 struct node * nxt;
14 }NODE;
15
16 NODE edg[MM];
17 NODE * link[NN];
18
19 int idx, N, S, T, D;
20 int dis[NN];
21 int stack[NN];
22 int mark[NN];
23 int cnt[NN];
24
25 int cmp( const void * a, const void * b){
26 NODE1 * aa = (NODE1 * )a;
27 NODE1 * bb = (NODE1 * )b;
28
29 return aa -> h - bb -> h;
30 }
31
32 void Add( int a, int b, int c){ // 加边
33 idx ++ ;
34 edg[idx].v = b;
35 edg[idx].wt = c;
36 edg[idx].nxt = link[a];
37 link[a] = edg + idx;
38 }
39
40 int Spfa(){
41 int u, v, i;
42 int top = 0 ;
43 memset(mark, 0 , sizeof (mark));
44
45 for (i = 1 ; i <= N; i ++ ){
46 dis[i] = INF;
47 }
48 for (i = 1 ; i <= N; i ++ ){ // 将所有点入栈,就不用建源点和所有点相连了
49 stack[ ++ top] = i;
50 cnt[i] = 1 ;
51 mark[i] = 1 ;
52 }
53 if (S < T) // 将标号小的置为0
54 dis[S] = 0 ;
55 else dis[T] = 0 ;
56
57 while (top){
58 u = stack[top -- ];
59 mark[u] = 0 ;
60 for (NODE * p = link[u]; p; p = p -> nxt){
61 v = p -> v;
62 if (dis[v] > dis[u] + p -> wt){
63 dis[v] = dis[u] + p -> wt;
64 if ( ! mark[v]){
65 stack[ ++ top] = v;
66 cnt[v] ++ ;
67 mark[v] = 1 ;
68 if (cnt[v] > N + 2 ) return - 1 ; // 当某一点入栈次数超过N(N 为节点数),表示有负环,我这里用了N + 2
69 }
70 }
71 }
72 }
73 if (S < T) // 输出标号大的
74 return dis[T];
75 else return dis[S];
76 }
77 void Solve(){
78 int i;
79 idx = 0 ;
80 memset(link, 0 , sizeof (link));
81 for (i = 2 ; i <= N; i ++ ){
82 Add(i, i - 1 , - 1 );
83 }
84
85 qsort(f + 1 , N, sizeof (f[ 0 ]), cmp);
86 S = f[ 1 ].id;
87 T = f[N].id;
88 for (i = 2 ; i <= N; i ++ ){
89 if (f[i - 1 ].id < f[i].id){ // 标号小的到标号大的建边,边权为D
90 Add(f[i - 1 ].id, f[i].id, D);
91 } else {
92 Add(f[i].id, f[i - 1 ].id, D);
93 }
94 }
95 printf( " %d\n " , Spfa());
96 }
97 int main()
98 {
99 int Tt, it, i;
100 scanf( " %d " , & Tt);
101 for (it = 1 ; it <= Tt; it ++ ){
102 scanf( " %d%d " , & N, & D);
103 for (i = 1 ; i <= N; i ++ ){
104 scanf( " %d " , & f[i].h);
105 f[i].id = i;
106 }
107 printf( " Case %d: " , it);
108 Solve();
109 }
110 return 0 ;
111 }
112
hdu3442 Three Kingdoms
BFS + 优先队列
开始看到这道题的时候感觉需要处理的太多,“果断”放弃,后来发现这题其实是最水的一道。
比赛的时候优先队列忘了,这是最大的杯具,基础不牢啊!
向队友学习 探索之美——接触优先队列
这题没什么好说的,易错的地方见代码。
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
2 #include < string .h >
3 #include < queue >
4 #include < iostream >
5 using namespace std;
6 #define INF 0xfffffff
7 struct node{
8 int x, y, c;
9 char vis[ 6 ];
10 bool operator < ( const node & a) const {
11 return a.c < c;
12 }
13 }sta, end;
14
15 int r, c;
16 int hurt[ 100 ]; // 保存每个字符的伤害值
17 int map0[ 52 ][ 52 ][ 100 ]; // map0[x][y][ch] 表示位置[x, y]会受到字符ch类攻击
18 char map[ 52 ][ 52 ];
19 char mark[ 52 ][ 52 ];
20 int dir[ 4 ][ 2 ] = { 0 , 1 , 1 , 0 , 0 , - 1 , - 1 , 0 };
21
22 int judge( int x, int y, char vis[]){ // 判断走到点[x, y]要受到的伤害值
23 int i, ans;
24 ans = 0 ;
25 for (i = ' A ' ; i <= ' E ' ; i ++ ){
26 if (map0[x][y][i] == 1 && ! vis[i - ' A ' ]){ // 如果已经收到此类过伤害,就不管了,否则,加上
27 vis[i - ' A ' ] = 1 ;
28 ans += hurt[i];
29 }
30 }
31 return ans;
32 }
33 int bfs(){
34 int i;
35 struct node cur, nxt;
36 priority_queue < struct node > pq;
37 pq.push(sta);
38 mark[sta.x][sta.y] = 1 ;
39 while ( ! pq.empty()){
40 cur = pq.top();
41 if (map[cur.x][cur.y] == ' ! ' ) return cur.c; // 如果在最少的伤害值时找到了出口,即可return
42 pq.pop();
43 for (i = 0 ; i < 4 ; i ++ ){
44 nxt.x = cur.x + dir[i][ 0 ];
45 nxt.y = cur.y + dir[i][ 1 ];
46 memcpy(nxt.vis, cur.vis, sizeof (cur.vis)); // 每个状态的受到的伤害情况也要传给后继节点,刚开始我就这地方错了
47 if (nxt.x >= 0 && nxt.x < r && nxt.y >= 0 && nxt.y < c){
48 if ( ! mark[nxt.x][nxt.y]){ // 一共就三个位置可以走,'C', '!', '.'
49 if (map[nxt.x][nxt.y] == ' C ' ){
50 mark[nxt.x][nxt.y] = 1 ;
51 nxt.c = cur.c + judge(nxt.x, nxt.y, nxt.vis);
52 pq.push(nxt);
53 } else if (map[nxt.x][nxt.y] == ' ! ' ){ // 这里不要标记,得到的可能不是最优的,不过这题数据好像可以在这里直接return
54 nxt.c = cur.c + judge(nxt.x, nxt.y, nxt.vis);
55 pq.push(nxt);
56 }
57 else if (map[nxt.x][nxt.y] == ' . ' ){
58 mark[nxt.x][nxt.y] = 1 ;
59 nxt.c = cur.c + judge(nxt.x, nxt.y, nxt.vis);
60 pq.push(nxt);
61 }
62 }
63 }
64 }
65 }
66 return - 1 ;
67 }
68
69 void dfs1( int x, int y, int step, char ch){ // 查找攻击范围
70 int i, cx, cy;
71 if (x < 0 || x >= r || y < 0 || y >= c) return ;
72 map0[x][y][ch] = 1 ;
73 if (step <= 0 ) return ; // 开始把这个语句放到上面的句子前面了,少考虑了好多点
74 for (i = 0 ; i < 4 ; i ++ ){
75 cx = x + dir[i][ 0 ];
76 cy = y + dir[i][ 1 ];
77 dfs1(cx, cy, step - 1 , ch);
78 }
79 }
80
81 int main(){
82 int T, i, j, it;
83 hurt[ ' A ' ] = 1 ;
84 hurt[ ' B ' ] = 2 ;
85 hurt[ ' C ' ] = 3 ;
86 hurt[ ' D ' ] = 4 ;
87 hurt[ ' E ' ] = 5 ;
88 scanf( " %d " , & T);
89 for (it = 1 ; it <= T; it ++ ){
90 scanf( " %d%d " , & r, & c);
91 for (i = 0 ; i < r; i ++ ){
92 scanf( " %s " , map[i]);
93 }
94 memset(map0, 0 , sizeof (map0));
95 for (i = 0 ; i < r; i ++ ){
96 for (j = 0 ; j < c; j ++ ){
97 if (map[i][j] == ' $ ' ){
98 sta.x = i; sta.y = j;
99 sta.c = 0 ;
100 } else if (map[i][j] == ' ! ' ){
101 end.x = i; end.y = j;
102 } else if (map[i][j] == ' A ' ){ // A有2步的攻击范围,用一个dfs来找到这些点并标记,B, C, D, E也一样
103 dfs1(i, j, 2 , ' A ' );
104 } else if (map[i][j] == ' B ' ){
105 dfs1(i, j, 3 , ' B ' );
106 } else if (map[i][j] == ' C ' ){
107 map0[i][j][ ' C ' ] = 1 ;
108 } else if (map[i][j] == ' D ' ){
109 dfs1(i, j, 2 , ' D ' );
110 } else if (map[i][j] == ' E ' ){
111 dfs1(i, j, 1 , ' E ' );
112 }
113 }
114 }
115 for (i = 0 ; i <= 5 ; i ++ ){ // 初始化
116 sta.vis[i] = 0 ;
117 }
118
119 memset(mark, 0 , sizeof (mark));
120 printf( " Case %d: %d\n " , it, bfs());
121 }
122 return 0 ;
123 }
124