非常可乐Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 24573 Accepted Submission(s): 9544 Problem Description 大家一定觉的运动以后喝可乐是一件很惬意的事情,但是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 Author seeyou Source |
bfs:
【解题思路】
因为要得到最少的次数,所以用bfs(dfs也可以,但是耗时长),易得当s为奇数时输出NO(奇数肯定不能平分呀),所以只需要讨论偶数的情况,分六种情况(每种情况有两种小情况)进行bfs搜索即可,记得做个标记,为了避免重复搜索,开二维即可,因为两个杯中的水量已知,第三个杯中的量就确定了。
(1)s倒入a,s倒完,a倒满
(2)s倒入b,s倒完,b倒满
(3)a倒入s,a倒完,s倒满
(4)a倒入b,a倒完,b倒满
(5)b倒入s,b倒完,s倒满
(6)b倒入a,b倒完,a倒满
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
const int INF=0x3f3f3f3f;
int vis[maxn][maxn];
int s,a,b;
struct Node
{
int s,a,b,step;
};
int bfs()
{
Node node;
node.s=s;node.a=0;node.b=0;node.step=0;
queue<Node>q;
q.push(node);
vis[a][b]=1;
while(!q.empty())
{
Node t=q.front();
Node v;
q.pop();
if(t.s==s/2 && t.a==s/2)
return t.step;
if(t.s && t.a!=a)//s倒入a
{
int x=a-t.a;
if(t.s>x)v.s=t.s-x,v.a=a;//a倒满
else v.s=0,v.a=t.a+t.s;//s倒光
v.b=t.b;v.step=t.step+1;
if(!vis[v.a][v.b])
{
vis[v.a][v,b]=1;
q.push(v);
}
}
if(t.s && t.b!=b)//s倒入b
{
int x=b-t.b;
if(t.s>x)v.s=t.s-x,v.b=b;//b倒满
else v.s=0,v.b=t.b+t.s;//s倒光
v.a=t.a;v.step=t.step+1;
if(!vis[v.a][v,b])
{
vis[v.a][v.b]=1;
q.push(v);
}
}
if(t.a && t.s!=s)//a倒入s
{
int x=s-t.s;
if(t.a>x)v.a=t.a-x,v.s=s;//s倒满
else v.a=0,v.s=t.s+t.a;//a倒光
v.b=t.b;v.step=t.step+1;
if(!vis[v.a][v.b])
{
vis[v.a][v.b]=1;
q.push(v);
}
}
if(t.a && t.b!=b)//a倒入b
{
int x=b-t.b;
if(t.a>x)v.a=t.a-x,v.b=b;//b倒满
else v.a=0,v.b=t.b+t.a;//a倒光
v.s=t.s;v.step=t.step+1;
if(!vis[v.a][v.b])
{
vis[v.a][v.b]=1;
q.push(v);
}
}
if(t.b && t.s!=s)//b倒入s
{
int x=s-t.s;
if(t.b>x)v.b=t.b-x,v.s=s;//s倒满
else v.b=0,v.s=t.s+t.b;//b倒光
v.a=t.a;v.step=t.step+1;
if(!vis[v.a][v.b])
{
vis[v.a][v.b]=1;
q.push(v);
}
}
if(t.b && t.a!=a)//b倒入a
{
int x=a-t.a;
if(t.b>x)v.b=t.b-x,v.a=a;//a倒满
else v.b=0,v.a=t.a+t.b;//b倒光
v.s=t.s;v.step=t.step+1;
if(!vis[v.a][v.b])
{
vis[v.a][v.b]=1;
q.push(v);
}
}
}
return 0;
}
int main()
{
while(~scanf("%d%d%d",&s,&a,&b) && s && a && b)
{
memset(vis,0,sizeof(vis));
if(a<b)swap(a,b);
int ans=bfs();
if(s%2 || !ans)printf("NO\n");
else printf("%d\n",ans);
}
return 0;
}
数论:
【解题思路】
转自:https://blog.csdn.net/v5zsq/article/details/52097459
数学厉害真好qaq膜拜大佬
设两个小瓶子容积分别为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。