文章目录

  • 非常可乐
    • 第一种解题方案(最麻烦但最通俗易懂的):
    • 第二种解题方案(优化bfs,减少不必要的计算)
      • 推理:
      • 优化:
    • 第三种解题方案(数论)

非常可乐

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=1495


大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出"NO"。


Input
三个整数 : S 可乐的体积 , N 和 M是两个杯子的容量,以"0 0 0"结束。
Output
如果能平分的话请输出最少要倒的次数,否则输出"NO"。
Sample Input
7 4 3
4 1 3
0 0 0
Sample Output
NO
3


题意:已经很明显了,我就没必要再说了,但u1s1,可乐自己一个人喝它不香吗?还有,要是see you 觉得喝可乐不惬意,那么直接给阿牛呗,题终!


第一种解题方案(最麻烦但最通俗易懂的):

由于是寻求最少倒水次数,所以我们应该使用bfs模拟六种情况搜索。分别是s->n;s->m;n->s;n->m;m->s;m->n.得到答案的条件其中两个剩余的量相同,剩下的一个为空。
由于此时的变化状态物有三个,所以我们要设置三维的辅助数组来判断某个状态是否已访问从而避免重复路径访问造成不必要的计算甚至死循环。同时设立结构体来存储某个状态的s,m,n和到达该状态的倒水次数。OK,此题就可以解决了。
PS:此题模拟六种情况的时候一定要细心,仔细分析判断。
AC代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<stack>
#include<queue>
#include<cstring>
#include<memory.h>
#include<map>
#include<iterator>
#include<list>
#include<set>
#include<functional>using namespace std;const int maxn=102;
struct node{int s,n,m;//可乐瓶和两个杯子在当前状态的剩余容量。int step;//倒到当前状态时所需次数。
};
bool visited[maxn][maxn][maxn];
int s,n,m;//可乐瓶的体积和两个杯子的容量。
void bfs(int s,int n,int m){queue<node> q;memset(visited,false,sizeof(visited));node temp,head;head.s=s;head.m=head.n=head.step=0;visited[s][0][0]=true;q.push(head);while(!q.empty()){head=q.front();if((head.s==head.m&&head.n==0)||(head.s==head.n&&head.m==0)||(head.n==head.m&&head.s==0)){cout<<head.step<<endl;return;}q.pop();for(int i=0;i<6;i++){//六种情况if(i==0){//s->n//足够if(head.s>=n-head.n){temp.n=n;temp.s=head.s-(n-head.n);temp.m=head.m;}//不够else{temp.n=head.n+head.s;temp.s=0;temp.m=head.m;}}else if(i==1){//s->m//足够if(head.s>=m-head.m){temp.m=m;temp.s=head.s-(m-head.m);temp.n=head.n;//足够if(head.s>=m-head.m){temp.m=m;temp.s=head.s-(m-head.m);temp.n=head.n;}//不够else{temp.m=head.m+head.s;temp.s=0;temp.n=head.n;}}//不够else{temp.m=head.m+head.s;temp.s=0;temp.n=head.n;}}else if(i==2){//n->s//足够if(head.n>=s-head.s){temp.s=s;temp.n=head.n-(s-head.s);temp.m=head.m;}//不够else{temp.s=head.s+head.n;temp.n=0;temp.m=head.m;}}else if(i==3){//n->m//足够if(head.n>=m-head.m){temp.m=m;temp.n=head.n-(m-head.m);temp.s=head.s;}//不够else{temp.m=head.m+head.n;temp.n=0;temp.s=head.s;}}else if(i==4){//m->s//足够if(head.m>=s-head.s){temp.s=s;temp.m=head.m-(s-head.s);temp.n=head.n;}//不够else{temp.s=head.m+head.s;temp.m=0;temp.n=head.n;}}else{//m->n//足够if(head.m>=n-head.n){temp.n=n;temp.m=head.m-(n-head.n);temp.s=head.s;}//不够else{temp.n=head.m+head.n;temp.m=0;temp.s=head.s;}}if(!visited[temp.s][temp.n][temp.m]){visited[temp.s][temp.n][temp.m]=true;temp.step=head.step+1;q.push(temp);}}}cout<<"NO"<<endl;return;
}
int main() {while(cin>>s>>n>>m&&(s+n+m)){bfs(s,n,m);}return 0;
}

第二种解题方案(优化bfs,减少不必要的计算)

推理:

我们思考一下,对于一瓶 容量为s=n+m的可乐,最后分完的结果是什么?这些可乐都分到哪去了?如果把可乐瓶也想象成杯子,那么显然可乐瓶的杯子最大,我们编号为1,接下来对两个已有的两个杯子判断,容量较大的为2,容量较小的为3.则1>2>=3.那么我们假设最后得到的结果3中有可乐,那么必然其中也要有2或1中有可乐,且它们的量要相等。可这显然不现实,因为3容量中最小,即使它满了,那么加相等的那个量也就只有2倍的3的容量是小于s(s=n+m)的。故最后3中一定是空的,我们只要考虑1和2是否相等以及3是否为空。OK,这就是我们的推理,对杯子编号,排序。

优化:

有了这个推理以后,我们再看看一开始1号杯是满的,其他两个杯是空的,又因为最后结果是1号杯的量的等于2号杯的量且3号杯为空,则如果1号杯的容量是奇数,它就无法对半分,因为水的刻度为整数,没办法,故我们在开始判断一下1号杯的量是否为奇数即可。我们设置一个倒水函数,因为我们已经对杯子编号了,所以可以通用,同样在倒水的设计上我们可采用简化的方式。(详细见代码),在判断的过程中我们只要考虑1号杯子和2号杯子的量是否相等以及3号是否为空即可。


AC代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<stack>
#include<queue>
#include<cstring>
#include<memory.h>
#include<map>
#include<iterator>
#include<list>
#include<set>
#include<functional>using namespace std;const int maxn=102;
int v[4];//为了简单判断,使这些杯子有规律性,我们编号
struct node{int v[4];//表示当前状态的三个杯子里的剩余量。int step;//记录步骤
};
node temp,head;
bool visited[maxn][maxn][maxn];//辅助数组,判断某个状态是否已经访问
void pour(int a,int b){//第a个杯子往第b个杯子里倒水。int sum=temp.v[a]+temp.v[b];//if(sum>=v[b]){//如果总量大于第b个杯子的容量,则temp.v[b]=v[b];}else{//小于则直接等于总容量。temp.v[b]=sum;}//最后第a个杯子剩余的容量。temp.v[a]=sum-temp.v[b];
}
void bfs(){//我们一定要v[1]>v[2]>=v[3]。queue<node> q;head.v[1]=v[1];head.v[2]=head.v[3]=head.step=0;memset(visited,false,sizeof(visited));visited[v[1]][0][0]=true;q.push(head);while(!q.empty()){head=q.front();q.pop();if(head.v[1]==head.v[2]&&head.v[3]==0){cout<<head.step<<endl;return;}for(int i=1;i<4;i++){for(int j=1;j<4;j++){//模拟倒水情况。if(i!=j){//自己肯定不能给自己倒水,故这里排除杯子编号相等的情况。temp=head;pour(i,j);if(!visited[temp.v[1]][temp.v[2]][temp.v[3]]){visited[temp.v[1]][temp.v[2]][temp.v[3]]=true;temp.step++;q.push(temp);}}}}}cout<<"NO"<<endl;
}
int main() {while(cin>>v[1]>>v[2]>>v[3]&&(v[1]+v[2]+v[3])){if(v[1]%2){cout<<"NO"<<endl;continue;}//判断是否为奇数。if(v[2]<v[3]){//为了保证我们这个规律:v[1]>v[2]>=v[3]。int t=v[3];v[3]=v[2];v[2]=t;}bfs();}return 0;
}

第三种解题方案(数论)

这种方法非常厉害,可惜我没看懂,但还是贴在这里吧。

分析:设两个小瓶子容积分别为a,b,问题转化成通过两个小瓶子的若干次倒进或倒出操作得到(a+b)/2体积的可乐,设两个小瓶子被倒进或倒出x次和y次(这里的x和y是累加后的操作,即x=第一个瓶子倒出的次数-倒进的次数,y=第二个瓶子倒出的次数-倒进的次数),那么问题转化成:

所以|x+|y|的最小值为(c+d)/2,通过x和y的通解形式显然可以看出x和y一正一负,不妨设x<0,那么就是往第一个小瓶子倒进x次,第二个小瓶子倒出y次,但是由于瓶子容积有限,所以倒进倒出操作都是通过大瓶子来解决的,一次倒进操作后为了继续使用小瓶子还要将小瓶子中可乐倒回大瓶子中,倒出操作同理,所以总操作次数是(c+d)/2*2=c+d,但是注意最后剩下的(a+b)/2体积的可乐一定是放在两个小瓶子中较大的那个中,而不是再倒回到大瓶子中,所以操作数要减一,答案就是c+d-1。


AC代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<stack>
#include<queue>
#include<cstring>
#include<memory.h>
#include<map>
#include<iterator>
#include<list>
#include<set>
#include<functional>using namespace std;int gcd(int a,int b){return b==0?a:gcd(b,a%b);
}
int main(){int a,b,c;while(cin>>a>>b>>c&&(a&&b&&c)){a/=gcd(b,c);if(a&1)cout<<"NO"<<endl;elsecout<<a-1<<endl;}return 0;
}

L - 非常可乐——HDU-1495(bfs->优化bfs->数论)相关推荐

  1. M - 非常可乐 HDU - 1495

    M - 非常可乐 HDU - 1495 一个简单的 bfs 被我写的又臭又长 如果修改的话,建议把倒水判断的部分直接改为 now.s+nown <= S 即可 #include<iostr ...

  2. 非常可乐 HDU - 1495

    大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为.因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多.但see ...

  3. HDU - 1495 非常可乐(BFS,数学)

    HDU - 1495 非常可乐(BFS,数学) 巨佬的数学解法 #include<iostream> using namespace std; int gcd(int a,int b) { ...

  4. HDU 1495 非常可乐

    - HDU 1495 - 非常可乐 Problem Description 大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为.因为每次当seeyou买了可乐以后,阿牛就要求和 ...

  5. [BZOJ 1098] [POI2007] 办公楼biu 【链表优化BFS】

    题目链接:BZOJ - 1098 题目分析 只有两个点之间有边的时候它们才能在不同的楼内,那么就是说如果两个点之间没有边它们就一定在同一座楼内. 那么要求的就是求原图的补图的连通块. 然而原图的补图的 ...

  6. 魔板 Magic Squares(bfs优化)

    [USACO3.2]魔板 Magic Squares 题目背景 在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板.这是一张有8个大小相同的格子的魔板: 1 2 3 4 8 7 6 5 题 ...

  7. L. Spicy Restaurant(多源BFS+递推)

    L. Spicy Restaurant 题目链接 https://codeforces.com/gym/103117/problem/L 思路: 1.考虑到1≤ai≤100 ,一开始直接使用BFS,对 ...

  8. HDU 1495 非常可乐(数论,BFS)

    非常可乐 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

  9. HDU 1495 非常可乐(BFS||数论)

    非常可乐 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

最新文章

  1. .NET基础示例系列之十六:制做进程监视器
  2. 如何通过交货单把B库存发出
  3. python基础教程第三版豆瓣-1024,程序媛/猿请查收!
  4. Leetcode 刷题笔记
  5. Appium移动自动化测试(一)--安装Appium(转)
  6. ViewPager中Fragment的重复创建、复用问题
  7. mongodb转实体对像_MongoDB:实体对象(javabean)转DBObject
  8. Project FreeEIM 2.0:重现失落的飞鸽传书
  9. 定时任务的选型及改造
  10. radio默认选中并显示相应信息 php,php实现select、radio、checkbox默认选择示例
  11. CodeProject上的一些摘抄
  12. MyEclipse10 破解方法
  13. 不会比这更详细的前端工程化的入门教程了
  14. 【视频】老外拍的阿里巴巴纪录片,讲述淘宝怎么打败eBay
  15. python利用selenium和safari浏览器驱动实现新浪微博自动点赞 Demo
  16. 第一台超高速巨型电子计算机,1997年,( )巨型机的研制成功,使我国在这个领域跨入了世界先进行列。...
  17. IE10,带您走进HTML5时代
  18. 机器学习:《统计学习方法》笔记(一)—— 隐马尔可夫模型
  19. Git之一次Push的回滚之旅
  20. 如何恢复电脑的administrator账户

热门文章

  1. Python str isdigit()方法
  2. 简历被肆意贩卖,个人信息安全何在?
  3. 【深度报道】用友企业云服务推出新品 打造中国企业云服务高地
  4. mtk设备模型之LCM--学习
  5. Java web基础学习笔记
  6. 人工智能革命(下):永生还是毁灭
  7. 求数组中最长递增子序列
  8. 用计算机谈歌曲凉凉,快速让你学会唱《凉凉》这首歌,千万不要错过哦!
  9. 51单片机《凉凉》高潮部分代码
  10. 深度学习环境配置_ubuntu18及以上