题解链接:http://page.renren.com/601081183/note/867254911
题目编号:hdu 4390~4399
这是杭电多校联合的最后一场比赛了,直到最后的这场比赛我们队在比赛中的题目数还是没有突破3题的局限。
今天比赛开始的时候,依然是我的队友读题,而我就随便看了一题在一边想。第一题看到的就是1001,一道排列组合的题目。这几天都有看排列组合的题,不过基本上都不能1y,所以这题我想了一下,翻了一下《组合数学》,然后就放弃了。1001的思路是有的,不过就是不会后期的计算。组合数学是我的弱项,之后的学习必须要加强!
然后就是队友看的1004,刚开始,我想着暴力寻找,用线段树更新,不过到后来才发现用线段树更新结点还是要变成逐个更新。于是,我就没辙了,丢给队友想。
这时,发现1007也有很多人过,所以我就改向去看1007了。听队友讲了一下题意,我基本明白了题目的意思。当时我想了一下最短路来算这个,不过好像不能得到准确的答案,而且操作起来又或者最后的还是有点麻烦的。于是,当时我就想到层次遍历,利用上一层的结果得到当前步数到达各点的最短时间,如果到满足要求的那一层,汇点的状态是无穷大,那么就是不可达。不过我想深一层的时候,发现有可能走多很多条路才得到最优解。于是,我又卡住了。不过很快的,我就想到一个特点,至少要走的那k层走够了以后,其余的部分不就是可以随便走,不过最后要走到汇点吗?然后我就倒过来想,求了以汇点作为源点到其他各点的最短路,然后将两个操作合并起来,最后不就可以得到满足题意的最优解了吗? 最终的代码很快就完成了,不过打错了一点东西,搞到我又debug了好久。不过庆幸的是,在我思路清晰的情况下,打出来的代码提交就是一个1y~赛后看了一下题解,貌似题解的方法会比我的慢上好多。
1007(hdu 4396):
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <vector> 5 #include <cmath> 6 #include <algorithm> 7 8 using namespace std; 9 const int maxn = 5002; 10 const int inf = 0x7f7f7f7f; 11 const int maxe = 100001; 12 13 struct edge{ 14 int s; 15 int t; 16 int cost; 17 void ins(int a, int b, int c){ 18 s = a; t = b; cost = c; 19 } 20 bool operator < (const edge &x) const{ 21 return s < x.s; 22 } 23 }E[maxe << 1]; 24 int dis[maxn], re[maxn], ld[maxn], dd[maxn]; 25 int q[maxn << 2], qh, qt; 26 bool inq[maxn]; 27 28 void spfa(int n, int s){ 29 int cur, es, et; 30 31 qh = qt = 0; 32 q[qt++] = s; 33 for (int i = 1; i <= n; i++){ 34 dd[i] = inf; 35 inq[i] = false; 36 } 37 dd[s] = 0; 38 39 while (qh < qt){ 40 cur = q[qh++]; 41 inq[cur] = false; 42 for (int i = re[cur]; i < re[cur + 1]; i++){ 43 es = E[i].s; 44 et = E[i].t; 45 if (dd[et] > dd[es] + E[i].cost){ 46 dd[et] = dd[es] + E[i].cost; 47 if (!inq[et]){ 48 q[qt++] = et; 49 inq[et] = true; 50 } 51 } 52 } 53 } 54 } 55 56 void pre_deal(int n, int m){ // 预处理每一条边,方便之后的使用 57 int p = 0; 58 59 sort(E, E + m); 60 for (int i = 0; i < m; i++){ 61 #ifndef ONLINE_JUDGE 62 printf("%d : %d %d %d\n", i, E[i].s, E[i].t, E[i].cost); 63 #endif 64 while (p <= E[i].s){ 65 re[p] = i; 66 p++; 67 } 68 } 69 for (int i = p; i <= n + 1; i++){ 70 re[i] = m; 71 } 72 #ifndef ONLINE_JUDGE 73 for (int i = 0; i <= n + 1; i++){ 74 printf("%d : %d\n", i, re[i]); 75 } 76 #endif 77 } 78 79 void deal(int n, int s, int t ,int k){ 80 int es, et; 81 82 for (int i = 1; i <= n; i++){ 83 ld[i] = inf; 84 dis[i] = inf; 85 } 86 ld[s] = 0; 87 while (k--){ 88 #ifndef ONLINE_JUDGE 89 puts("status:"); 90 for (int i = 1; i <= n; i++){ 91 printf("%d: %d\n", i, ld[i]); 92 } 93 #endif 94 for (int i = 1; i <= n; i++){ 95 if (ld[i] != inf){ 96 for (int j = re[i]; j < re[i + 1]; j++){ 97 es = E[j].s; 98 et = E[j].t; 99 if (dis[et] > ld[es] + E[j].cost) 100 dis[et] = ld[es] + E[j].cost; 101 } 102 } 103 } 104 for (int i = 1; i <= n; i++){ 105 ld[i] = dis[i]; 106 dis[i] = inf; 107 } 108 } 109 } 110 111 int final(int n){ 112 int ret = inf; 113 114 for (int i = 1; i <= n; i++){ 115 #ifndef ONLINE_JUDGE 116 printf("%d : %d %d\n", i, dd[i], ld[i]); 117 #endif 118 if (dd[i] != inf && ld[i] != inf){ 119 if (ret > dd[i] + ld[i]) 120 ret = dd[i] + ld[i]; 121 } 122 } 123 124 if (ret == inf) return -1; 125 return ret; 126 } 127 128 int main(){ 129 int n, m; 130 int s, t, c; 131 int i1, i2; 132 133 while (~scanf("%d%d", &n, &m)){ 134 for (int i = 0; i < m; i++){ 135 scanf("%d%d%d", &s, &t, &c); 136 i1 = i << 1; i2 = i << 1 | 1; 137 E[i1].ins(s, t, c); 138 E[i2].ins(t, s, c); // 双向边 139 } 140 m <<= 1; 141 pre_deal(n, m); 142 scanf("%d%d%d", &s, &t, &c); 143 c = (c - 1) / 10 + 1; 144 deal(n, s, t, c); // 正向遍历最少经过路的数目,不超过50 145 spfa(n, t); // 反向找到到汇点的所有最短路 146 printf("%d\n", final(n)); // 结合两个结果得到答案 147 } 148 149 return 0; 150 }
在我打1007的时候,队友想到了1004其实在500次暴力枚举后,每个人的速度就会稳定,之后就不会出现人与人间超越的情况了。当时我想了一下,觉得好像挺有道理的,于是在我想到spfa前就让队友打出他的代码。不过打出来的代码提交wa了,后来我想到1007的解法了,我就拿过键盘,队友在隔壁的电脑查看自己的代码。在我打完1007并且通过了以后,队友还在卡1004。于是我就提出我重新打这个程序的要求。当然,他们也同意了。在我快打完的时候,我突然想到队友的代码缺少了一个条件,于是我就问了一下他是否有写排编号的条件。果然,队友添加上去后就ac了!不过我也尝试着交我的代码,不过是wa的......- - 赛后检查了代码,才发现原来是排序写少了一个+1,所以wa了,过过来就ac了!
1004(hdu 4393):
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 #include <cstdlib> 6 7 using namespace std; 8 9 const int maxn = 50005; 10 const int inf = 0x7f7f7f7f; 11 12 struct point{ 13 int cur; 14 int s; 15 int num; 16 bool operator < (const point &x) const { 17 if (cur != x.cur) return cur > x.cur; 18 return num < x.num; 19 } 20 }p[maxn]; 21 22 void deal(int cc){ 23 int n, t; 24 int maxcur, mk; 25 bool fs = true; 26 27 scanf("%d", &n); 28 for (int i = 1; i <= n; i++){ 29 scanf("%d%d", &p[i].cur, &p[i].s); 30 p[i].num = i; 31 } 32 printf("Case #%d:\n", cc); 33 if (n <= 550) t = n; 34 else t = 550; 35 for (int i = 1; i <= t; i++){ // 暴力搜索未稳定时的序列 36 maxcur = -inf; 37 for (int j = 1; j <= n; j++){ 38 if (maxcur < p[j].cur){ 39 maxcur = p[j].cur; 40 mk = j; 41 } 42 p[j].cur += p[j].s; 43 } 44 p[mk].cur = -inf; 45 if (fs){ 46 printf("%d", mk); 47 fs = false; 48 } 49 else printf(" %d", mk); 50 } 51 if (n > 550){ 52 sort(p + 1, p + n + 1); 53 for (int i = 1, endi = n - 550; i <= endi; i++){ 54 printf(" %d", p[i].num); 55 } 56 } 57 puts(""); 58 59 } 60 61 int main(){ 62 int T; 63 64 scanf("%d", &T); 65 for (int i = 1; i <= T; i++){ 66 deal(i); 67 } 68 69 return 0; 70 }
过了这题,还剩一个半小时,于是我们就集中想了1005。当时已经想到了逐位搜索,不过就是没有总结出一个系统的做法,所以在最后40分钟的时候,我尝试着逐步打出来,不过最后还是打出了一个烂程序,连sample都不能过。回到宿舍以后再打,wa了好几次。在debug的时候,发现我的代码要加好多补丁,所以我就直接放弃了代码,用另一种方法来打这题!
1005在确定最后第k位的时候,当前得到的数的平方的最后k位必须要和目标尾数的最后k位相同才能继续进行下去。然后dfs一下,得到下面的代码!
1005(hdu 4394):
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 #include <cstdlib> 6 7 using namespace std; 8 9 const int maxn = 50005; 10 const int inf = 0x7f7f7f7f; 11 12 struct point{ 13 int cur; 14 int s; 15 int num; 16 bool operator < (const point &x) const { 17 if (cur != x.cur) return cur > x.cur; 18 return num < x.num; 19 } 20 }p[maxn]; 21 22 void deal(int cc){ 23 int n, t; 24 int maxcur, mk; 25 bool fs = true; 26 27 scanf("%d", &n); 28 for (int i = 1; i <= n; i++){ 29 scanf("%d%d", &p[i].cur, &p[i].s); 30 p[i].num = i; 31 } 32 printf("Case #%d:\n", cc); 33 if (n <= 550) t = n; 34 else t = 550; 35 for (int i = 1; i <= t; i++){ 36 maxcur = -inf; 37 for (int j = 1; j <= n; j++){ 38 if (maxcur < p[j].cur){ 39 maxcur = p[j].cur; 40 mk = j; 41 } 42 p[j].cur += p[j].s; 43 } 44 p[mk].cur = -inf; 45 if (fs){ 46 printf("%d", mk); 47 fs = false; 48 } 49 else printf(" %d", mk); 50 } 51 if (n > 550){ 52 sort(p + 1, p + n + 1); 53 for (int i = 1, endi = n - 550; i <= endi; i++){ 54 printf(" %d", p[i].num); 55 } 56 } 57 puts(""); 58 59 } 60 61 int main(){ 62 int T; 63 64 scanf("%d", &T); 65 for (int i = 1; i <= T; i++){ 66 deal(i); 67 } 68 69 return 0; 70 }
这次数学知识占的比例比较高,以后的目标还是要在搞好基础算法的同时,训练好数学思维!
——written by Lyon