uva10603 Fill 倒水问题 解题报告:
题目链接:https://uva.onlinejudge.org/external/106/10603.pdf
题目大意:
设3个杯子的容量为abc,起初只有第三个杯子装满了c升水。其它两个杯子均为空。最少要倒多少升水可以让某一个杯子里有d升水。如果无法做到d升水。就让某个杯子里有d‘升水,其中d’<d而且尽量接近d(1<=a,b,c,d<=200)要求输出最小的倒水量和目标水量(d或者是d‘)
分析:
这个样子其实有一点点动态规划的味道,其实不是。一看到这种求最小的倒水量。第一眼就应该想到用BFS。广搜,可以完美的设计到这种状态或者是抉择问题。而这道题。3个杯子,假设在某一时刻第一个杯子里有v1升水。第二个杯子有v2升水,第三个杯子有v3升水。而这个时候可以说是在某一时刻的状态。“状态”这个名词很玄学,往往有很多的算法和思想都会运用到状态这个概念。而每个状态之间都可以通过某种方式进行转换,这道题就是通过倒水。
循环来两,两倒水,倒出后的结果放到一个新的状态,新的状态进队。(进队之前要判重。)理论上就有 (a+1)(b+1)(c+1)=8120601种状态,这个状态有点多。内存有点多。但是呢,这种判断是不精确的。因为,水就那么多。当a,b的量确定之后,c的量就确定了,所以这么大的状态就一下缩小到201^2=40401;这就小多了。
而在一般的BFS中目标状态最先搜索到的一定是步骤最小的一个目标状态,而这道题求的不是最小步骤的目标状态而是倒水量最小的目标状态,所以,这里的队列应该用到优先队列,优先队列里的判断条件就是根据每种状态的倒水量的大小,来判断。
1 #include<cstdio> 2 #include<algorithm> 3 #include<string.h> 4 #include<queue> 5 using namespace std; 6 struct node{ //每种状态 7 int v[4]; 8 int num; 9 }start; 10 bool operator < (const node &a,const node &b) //这个东西是用来给优先队列判断优先级用的。很迷,不是很懂。 11 { 12 return a.num>b.num; 13 } 14 int judge[4],d,ans[205]; 15 int visit[201][201],mark[201][201]; 16 int until_ans(node n)//当一种状态出现的时候,要对里面的每一杯水之后的倒水量进行一个存储,ans[i]代表当杯子里出现i升水时,最小的倒水量。 17 { 18 for(int i=1;i<=3;i++) 19 { 20 int b; 21 b=n.v[i]; 22 if(ans[b]<0||n.num<ans[b])ans[b]=n.num; 23 } 24 return 0; 25 } 26 void bfs() 27 { 28 priority_queue<node> q; 29 memset(visit,0,sizeof(visit)); 30 memset(mark,0,sizeof(mark)); 31 start.v[1]=0;start.v[2]=0;start.v[3]=judge[3];start.num=0; 32 q.push(start); //初始状态 33 visit[0][0]=1; 34 while(!q.empty()) 35 { 36 node now=q.top(); 37 q.pop(); 38 if(mark[now.v[1]][now.v[2]])continue; 39 mark[now.v[1]][now.v[2]]=1; 40 until_ans(now);//记录出现杯子里的水的时候,总倒水量。 41 if(ans[d]>0)break;//找到目标状态。 42 for(int i=1;i<=3;i++)//枚举杯子 43 for(int j=1;j<=3;j++) 44 if(i!=j)//自己不能给自己倒 45 { 46 node now_1; 47 memcpy(&now_1,&now,sizeof(now)); 48 if(now_1.v[i]==0||now_1.v[j]==judge[j])continue;//当当前状态要倒出的杯子没水或者被倒水的杯子满的就不能倒。 49 int water=min(judge[j],now_1.v[i]+now_1.v[j])-now_1.v[j];//判断到底倒多少水。这个得自己理解。 50 now_1.num+=water; 51 now_1.v[i]-=water; 52 now_1.v[j]+=water; 53 if(!visit[now_1.v[1]][now_1.v[2]])//如果当前状态没出现,就入队。 54 { 55 q.push(now_1); 56 visit[now_1.v[1]][now_1.v[2]]=1; 57 } 58 } 59 } 60 while(d>=0)//枚举答案。 61 { 62 if(ans[d]>=0){ 63 printf("%d %d\n",ans[d],d); 64 return ; 65 } 66 d--;//当目标答案没有的时候,就往前找。(依照题目要求) 67 } 68 } 69 int main() 70 { 71 int n; 72 scanf("%d",&n); 73 while(n--) 74 { 75 scanf("%d%d%d%d",&judge[1],&judge[2],&judge[3],&d); 76 memset(ans,-1,sizeof(ans)); 77 bfs(); 78 } 79 return 0; 80 }