uva10603 解题报告

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 }

 

 

转载于:https://www.cnblogs.com/uncle-lu/p/5884766.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值