L-非常可乐 BFS
作者:互联网
题目
大家一定觉的运动以后喝可乐是一件很惬意的事情,但是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
思路
题目要求你利用三个容量分别为S,A,B的杯子(S=A+B),起始时S装满,A,B为空. 通过六种操作
S->A
S->B
A->S
A->B
B->A
B->S
最后使得其中两个容器中的可乐体积相等,并且第三个容器可乐体积为空
方案1
我们可以用BFS直接无脑去模拟这六种操作,最后只要判断有两个杯子体积相等,剩下一个为空,倒的时候要小心,不要算错了
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <map>
#include <queue>
#include <vector>
#define endl '\n'
using namespace std;
void IOS(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
}
typedef pair<int,int> PII;
typedef long long ll;
const int maxn=1e5+5;
int vis[110][110][110];
int S,A,B;
struct node{
int s,a,b;
int step;
}now,front;
void bfs()
{
now.s=S;
now.a=0;
now.b=0;
now.step=0;
queue<node>q;
q.push(now);
vis[S][0][0]=1;
while(!q.empty())
{
front=q.front();
q.pop();
if((front.s==front.a&&front.b==0)||(front.s==front.b&&front.a==0)||(front.b==front.a&&front.s==0))
{ //cout<<front.s<<' '<<front.a<<' '<<front.b<<endl;
cout<<front.step<<endl;
return;
}
//共有六种操作
for(int i=1;i<=6;i++)
{
//s->a
now=front;
if(i==1)
{
//如果未满
if(now.s+now.a<=A)
{
now.a=now.a+now.s;
now.s=0;
}
//如果满了,还有剩余
else
{
now.s= now.a+now.s-A;
now.a= A;
}
}
//s->b
else if(i==2)
{
//如果未满
if(now.s+now.b<=B)
{
now.b=now.b+now.s;
now.s=0;
}
//如果满了,还有剩余
else
{
now.s= now.s+now.b-B;
now.b= B;
}
}
//a->s
else if(i==3)
{
//如果未满
if(now.s+now.a<=S)
{
now.s=now.a+now.s;
now.a=0;
}
//如果满了,还有剩余
else
{
now.a= now.a+now.s-S;
now.s= S;
}
}
//a->b
else if(i==4)
{
//如果未满
if(now.b+now.a<=B)
{
now.b=now.a+now.b;
now.a=0;
}
//如果满了,还有剩余
else
{
now.a= now.a+now.b-B;
now.b= B;
}
}
//b->a
else if(i==5)
{
//如果未满
if(now.b+now.a<=A)
{
now.a=now.a+now.b;
now.b=0;
}
//如果满了,还有剩余
else
{
now.b= now.a+now.b-A;
now.a= A;
}
}
//b->s
else if(i==6)
{
//如果未满
if(now.s+now.b<=S)
{
now.s=now.b+now.s;
now.b=0;
}
//如果满了,还有剩余
else
{
now.b= now.b+now.s-S;
now.s= S;
}
}
// cout<<i<<' '<<front.s<<' '<<front.a<<' '<<front.b<<endl;
now.step=front.step+1;
if(!vis[now.s][now.a][now.b])
{ vis[now.s][now.a][now.b]=1;
q.push(now);
}
}
}
cout<<"NO"<<endl;
return ;
}
int main(){
while(~scanf("%d%d%d",&S,&A,&B))
{memset(vis,0,sizeof(vis));
if(S==0&&A==0&&B==0)
{
break;
}
bfs();
}
return 0;
}
方案二(稍加分析,简化计算)
由于S=A+B;我们设S,A,B三个杯子容量 S>A>=B
若要使得最后有两个体积相等 那么S一定是奇数
并且一定是S == A (不考虑A==B的情况);
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <map>
#include <queue>
#include <vector>
#define endl '\n'
using namespace std;
void IOS(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
}
typedef pair<int,int> PII;
typedef long long ll;
const int maxn=1e5+5;
int vis[110][110][110];
int S,A,B;
struct node{
int s,a,b;
int step;
}now,front;
void bfs()
{
now.s=S;
now.a=0;
now.b=0;
now.step=0;
queue<node>q;
q.push(now);
vis[S][0][0]=1;
while(!q.empty())
{
front=q.front();
q.pop();
if(front.s==front.a&&front.b==0)
{ //cout<<front.s<<' '<<front.a<<' '<<front.b<<endl;
cout<<front.step<<endl;
return;
}
//共有六种操作
for(int i=1;i<=6;i++)
{
//s->a
now=front;
if(i==1)
{
//如果未满
if(now.s+now.a<=A)
{
now.a=now.a+now.s;
now.s=0;
}
//如果满了,还有剩余
else
{
now.s= now.a+now.s-A;
now.a= A;
}
}
//s->b
else if(i==2)
{
//如果未满
if(now.s+now.b<=B)
{
now.b=now.b+now.s;
now.s=0;
}
//如果满了,还有剩余
else
{
now.s= now.s+now.b-B;
now.b= B;
}
}
//a->s
else if(i==3)
{
//如果未满
if(now.s+now.a<=S)
{
now.s=now.a+now.s;
now.a=0;
}
//如果满了,还有剩余
else
{
now.a= now.a+now.s-S;
now.s= S;
}
}
//a->b
else if(i==4)
{
//如果未满
if(now.b+now.a<=B)
{
now.b=now.a+now.b;
now.a=0;
}
//如果满了,还有剩余
else
{
now.a= now.a+now.b-B;
now.b= B;
}
}
//b->a
else if(i==5)
{
//如果未满
if(now.b+now.a<=A)
{
now.a=now.a+now.b;
now.b=0;
}
//如果满了,还有剩余
else
{
now.b= now.a+now.b-A;
now.a= A;
}
}
//b->s
else if(i==6)
{
//如果未满
if(now.s+now.b<=S)
{
now.s=now.b+now.s;
now.b=0;
}
//如果满了,还有剩余
else
{
now.b= now.b+now.s-S;
now.s= S;
}
}
// cout<<i<<' '<<front.s<<' '<<front.a<<' '<<front.b<<endl;
now.step=front.step+1;
if(!vis[now.s][now.a][now.b])
{ vis[now.s][now.a][now.b]=1;
q.push(now);
}
}
}
cout<<"NO"<<endl;
return ;
}
int main(){
while(~scanf("%d%d%d",&S,&A,&B))
{memset(vis,0,sizeof(vis));
if(S==0&&A==0&&B==0)
{
break;
}
if(S%2==1)
{
cout<<"NO"<<endl;
continue;
}
else
{
if(A<B)
swap(A,B);
bfs();
}
}
return 0;
}
方案三(数论)
看大佬的,还是不懂 @$@
设两个小水杯容积分别为A,B,问题转化成通过两个小水杯的若干次倒进或倒出操作最后得到(A+B)/2体积的可乐,设两个小水杯被倒进或倒出x次和y次(注意这里的x是一个杯子的净操作,即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。
#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;
else
cout<<a-1<<endl;
}
return 0;
}
标签:非常,include,int,else,BFS,front,now,未满,可乐 来源: https://www.cnblogs.com/Xiaomostream/p/15083251.html