题意:有3个有一定容量的杯子,给定一个水的量。问是否能让一个杯子装这个给定容量的水,以及所需要倒来倒去的水的最小量。如果不能,则能倒出的比给定容量小但最接近的量,以及所需要进行倒的水的最小量。
思路:虽然是隐式图搜索的问题,其实和之前数据结构基础章节的图的题很类似,就是一个状态转换,然后深搜或宽搜。只不过这里状态的变换不是像之前那样是固定的,可以用一个多维数组来表示;这里的变换是倒水后三个杯子的容量变化。其实题目中也明确给出了状态的变化,要么某水杯倒完,要么另一水杯被装满。比如a倒向b,可以像下面这样写:
//a to b if(hd.sa>0) { int cha=b-hd.sb; if(hd.sa>=cha) newhd={hd.sa-cha,b,hd.sc}; else newhd={0,hd.sb+hd.sa,hd.sc}; } if(vis[newhd.sa][newhd.sb]==0) { queue[rear++]=newhd; }
但是之后a倒向c等等,与此类似,但又需要重新写一个类似的代码而不能重用这段代码。要重用的话,用一个数组来表示结构体的sa、sb、sc。这样用数组来表示其成员的方法在之前数据结构基础图相关的题中就已经见过,比如迷宫的四个方向等。
但是写完之后通过了网上能找到的所有测试数据,还是WA。。卡了太长时间。最后,这里有所帮助。主要是考虑到两个相同的状态,可能后来出现的状态的倒水量比已经出现过该状态时的倒水量要小,这样的话,虽然该状态已出现过,还是需要把该状态加入队列进行扩展;否则你找到的就不是最小的倒水量。网上大部分的题解应该都是WA了,此题应该后来加强了数据。。因为要找的是最小的到水量,重复状态的处理是一个方面,还有一个方面是bfs进行遍历时找到目标值后并不能终止,因为bfs找的是最少次数的,并不一定是最小倒水量的,必须把所有结点扩展完。
综上,需要注意的点如下:
1.bfs或dfs需要遍历完所有结点,不能提前跳出。
2.重复状态出现时,需要比较两者的倒水量。如果后者的倒水量较小,还是需要重新访问。(有的用hash判重貌似可以;紫书上记录所有解,结束后再找最优解,当然就不存在这个问题了。。)
后面附上一些数据,但是没有检测出第二个注意项。。
Code:
#include<stdio.h> #include<string.h> typedef struct node { int s[3]; int pour; } stat; void solve(); int jug[3],d; stat queue[10000000]; //int pour[10000]; int vis[205][205];//标记状态的a、b值 int visvalue[205][205];//该状态之前访问过的pour值 int closepour,closed; int main() { //freopen("10603.in","r",stdin); //freopen("10603.out","w",stdout); int t; scanf("%d",&t); while(t-->0) { scanf("%d%d%d%d",&jug[0],&jug[1],&jug[2],&d); memset(vis,0,sizeof(vis)); closepour=closed=0; solve(); } return 0; } void solve() { int front=0; int rear=0; //queue[rear++]={{0,0,jug[2]},0}; stat ys={0,0,jug[2],0}; queue[rear++]=ys; vis[0][0]=1; visvalue[0][0]=0; int flag=0;//是否找到d值 while(front<rear) { stat hd=queue[front++]; if(hd.s[0]==d || hd.s[1]==d || hd.s[2]==d) { if(flag==0) { closed=d; closepour=hd.pour; } else closepour=hd.pour<closepour?hd.pour:closepour; flag=1; //break; } if(flag==0) for(int i=0;i<3;++i) { int tempd=hd.s[i]; if(tempd<d && tempd>closed) { closed=tempd; closepour=hd.pour;} else if(tempd==closed) closepour=hd.pour<closepour?hd.pour:closepour; } for(int i=0;i<3;++i)//把i往j倒 for(int j=0;j<3;++j) { int cha=jug[j]-hd.s[j];//把j填满所需的差值 if(hd.s[i]<=0 || i==j || cha==0) continue; stat newhd=hd; if(hd.s[i]>=cha) { //stat newhd=hd; newhd.s[i]=newhd.s[i]-cha; newhd.s[j]=jug[j]; newhd.pour+=cha; } else { newhd.pour+=newhd.s[i]; newhd.s[j]=newhd.s[j]+newhd.s[i]; newhd.s[i]=0;//注意赋值顺序 } if(!vis[newhd.s[0]][newhd.s[1]] || visvalue[newhd.s[0]][newhd.s[1]]>newhd.pour) { queue[rear++]=newhd; vis[newhd.s[0]][newhd.s[1]]=1; visvalue[newhd.s[0]][newhd.s[1]]=newhd.pour; } }//for }//while printf("%d %d\n",closepour,closed); }
Code://最开始写的、没写完的版本,之后改为上面的、用数组代替几个变量、易于代码重用的版本。
#include<stdio.h> typedef struct node { int sa,sb,sc; } stat; int a,b,c,d; stat queue[10000]; int vis[205][205];//标记状态的a、b值 int main() { int t; scanf("%d",&t); while(t-->0) { scanf("%d%d%d%d",&a,&b,&c,&d); memset(); } return 0; } void solve(int a,int b,int c) { int front=0; int rear=0; queue[rear++]={0,0,c}; vis[0][0]=1; while(front<rear) { stat hd=queue[front++]; if(hd.sa==d || hd.sb==d || hd.sc==d) { break; } stat newhd; for(int i=0;i<3;++i) for(int j=0;j<3;++j) { if(i==j) continue; } //a to b if(hd.sa>0) { int cha=b-hd.sb; if(hd.sa>=cha) newhd={hd.sa-cha,b,hd.sc}; else newhd={0,hd.sb+hd.sa,hd.sc}; } if(vis[newhd.sa][newhd.sb]==0) { queue[rear++]=newhd; } } }
input:
119
33 12 113 6
11 135 70 15
15 153 184 60
184 180 180 146
167 57 83 60
181 32 50 72
85 99 132 34
13 38 109 79
26 53 14 12
143 37 165 83
73 134 189 126
29 34 91 169
23 172 67 173
127 180 181 116
168 188 79 113
175 181 4 164
76 177 11 17
102 19 190 175
125 151 130 179
171 131 161 21
121 170 80 28
151 25 150 61
169 119 13 29
9 121 77 16
40 85 98 88
45 118 80 89
31 170 46 10
12 2 78 7
195 109 148 157
103 146 133 98
138 72 42 145
7 146 192 119
132 23 31 67
97 177 141 88
170 127 124 186
135 158 105 178
127 130 67 30
121 7 53 200
14 36 78 95
91 196 159 47
77 23 19 189
100 151 176 136
78 70 55 100
166 190 28 137
70 94 97 94
22 167 100 150
178 12 4 155
128 69 184 162
116 182 92 3
122 16 133 45
124 199 62 187
159 91 166 89
190 6 161 64
169 188 172 38
137 148 199 175
162 66 193 183
107 55 48 62
144 29 70 157
196 46 5 131
12 132 113 171
88 178 137 74
85 58 166 110
102 199 182 142
38 41 65 175
46 121 96 89
74 166 149 165
101 47 188 153
151 89 179 101
1 17 67 93
151 65 74 10
8 30 64 33
170 22 71 105
108 192 142 166
22 158 158 68
25 99 6 124
54 153 188 161
42 34 169 55
52 169 98 20
43 36 199 138
13 189 58 70
45 120 181 176
40 43 56 116
198 42 118 38
138 29 194 106
182 179 39 163
51 11 148 113
170 70 46 124
34 182 59 80
133 78 80 40
172 172 97 135
98 147 14 15
147 36 152 8
104 106 191 191
41 132 116 116
195 187 2 139
54 29 170 38
138 186 83 27
171 110 159 179
134 69 34 149
153 58 81 185
187 35 164 72
12 23 42 14
12 53 75 23
56 23 34 2
23 55 72 25
124 86 173 84
102 7 109 33
16 116 148 52
121 61 159 140
1 15 17 10
2 13 17 9
14 2 16 11
1 11 13 10
1 7 8 3
0 0 0 0
1 3 6 4
2 3 4 2
96 97 199 62
1 1 1 3
output:
171 6
99 15
120 60
0 0
57 57
0 50
99 33
503 79
0 0
989 81
1850 121
0 91
0 67
127 54
0 79
0 4
0 11
1851 175
0 130
0 0
0 0
100 50
0 13
117 14
490 88
0 80
0 0
12 6
0 148
103 30
0 42
238 119
0 31
97 44
0 124
0 105
0 0
0 53
0 78
0 0
0 19
351 125
0 55
0 28
94 94
0 100
0 4
1199 158
0 0
186 43
0 62
91 75
120 60
169 3
1213 170
426 163
0 48
0 70
0 5
0 113
88 49
7196 110
102 102
0 65
46 50
0 149
1445 153
89 90
0 67
65 9
56 32
0 71
0 142
132 66
0 6
1404 161
42 42
0 0
2279 138
0 58
375 166
0 56
126 34
1480 106
0 39
695 113
0 46
0 59
78 2
0 97
0 14
252 8
0 191
0 116
0 2
705 38
0 0
0 159
0 34
0 81
140 70
166 14
728 23
0 0
23 23
850 77
757 33
176 52
6912 140
13 10
14 9
10 10
5 10
6 3
0 0
3 4
2 2
9859 62
0 1