题解链接:http://page.renren.com/601081183/note/867254911
题目编号:hdu 4390~4399
这是杭电多校联合的最后一场比赛了,直到最后的这场比赛我们队在比赛中的题目数还是没有突破3题的局限。
今天比赛开始的时候,依然是我的队友读题,而我就随便看了一题在一边想。第一题看到的就是1001,一道排列组合的题目。这几天都有看排列组合的题,不过基本上都不能1y,所以这题我想了一下,翻了一下《组合数学》,然后就放弃了。1001的思路是有的,不过就是不会后期的计算。组合数学是我的弱项,之后的学习必须要加强!
然后就是队友看的1004,刚开始,我想着暴力寻找,用线段树更新,不过到后来才发现用线段树更新结点还是要变成逐个更新。于是,我就没辙了,丢给队友想。
这时,发现1007也有很多人过,所以我就改向去看1007了。听队友讲了一下题意,我基本明白了题目的意思。当时我想了一下最短路来算这个,不过好像不能得到准确的答案,而且操作起来又或者最后的还是有点麻烦的。于是,当时我就想到层次遍历,利用上一层的结果得到当前步数到达各点的最短时间,如果到满足要求的那一层,汇点的状态是无穷大,那么就是不可达。不过我想深一层的时候,发现有可能走多很多条路才得到最优解。于是,我又卡住了。不过很快的,我就想到一个特点,至少要走的那k层走够了以后,其余的部分不就是可以随便走,不过最后要走到汇点吗?然后我就倒过来想,求了以汇点作为源点到其他各点的最短路,然后将两个操作合并起来,最后不就可以得到满足题意的最优解了吗? 最终的代码很快就完成了,不过打错了一点东西,搞到我又debug了好久。不过庆幸的是,在我思路清晰的情况下,打出来的代码提交就是一个1y~赛后看了一下题解,貌似题解的方法会比我的慢上好多。
1007(hdu 4396):
View Code
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):
View Code
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):
View Code
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
来源:https://www.cnblogs.com/LyonLys/archive/2012/08/23/2012MUTC10_Lyon.html
