题意:
给定两个容量分别为 A 升和 B 升的罐子。可以执行以下操作:
-
F I L L ( i ) FILL(i) FILL(i) 从水龙头向罐子 i ( 1 ≤ i ≤ 2 ) i (1 ≤ i ≤ 2) i(1≤i≤2) 倒水;
-
D R O P ( i ) DROP(i) DROP(i) 把罐子 i 的水倒掉;
-
P O U R ( i , j ) POUR(i,j) POUR(i,j) 从罐子 i i i 向罐子 j j j 倒水;执行完这个操作后,要么罐子 j j j 满了(罐子 i i i 里可能还有一些水),要么罐子 i i i 为空(罐子 i i i 的所有水都被倒到了罐子 j j j 里)。
编写一个程序,找出能够通过最短的操作序列,使得两个罐子中的任意一个恰好有 C 升水。
输入
第一行包含三个整数 A, B, 和 C。这些数都在
1
1
1 到
100
100
100 的范围内,且
C
≤
m
a
x
(
A
,
B
)
C≤max(A,B)
C≤max(A,B) 。
输出
输出的第一行必须包含操作序列的长度
K
K
K。接下来的
K
K
K 行分别描述一个操作。如果有多个最短长度的操作序列,输出任意一个即可。如果无法达到期望的结果,输出文件的第一行必须包含单词
‘
i
m
p
o
s
s
i
b
l
e
’
‘impossible’
‘impossible’。
思路
简化一下题意就是给定两个数AB,求二者不断相加取模去凑出C的最小步数
何时有解
考虑当 A = 3 , B = 4 A=3,B=4 A=3,B=4 时,C等于任何数均有合法解。因为 C % B = X ( X ∈ { 0 , 1 , 2 , 3 } ) C\%B=X(X∈\{0,1,2,3\}) C%B=X(X∈{0,1,2,3}) ,而 n ∗ A % B n*A\%B n∗A%B 一定可以可以凑出 C C C 缺的那个余数。
进一步考虑什么时候不行,当余数缺的时候一定不行,例如 2 , 4 2,4 2,4一定凑不出 5 5 5 ,因为 5 % 4 = 1 5\%4=1 5%4=1 而显然 2 2 2 和 4 4 4 无论如何相加取模也凑不出 1 1 1 。由此不难看出当 C % g c d ( a , b ) = 0 C\%gcd(a,b)=0 C%gcd(a,b)=0 时,一定有合法解,反之输出“impossible”。
何时最优
经过上述分析,我们可以将问题转化为凑出那个合法模数的最少步数。而模数推一推可以发现它在循环完一层之前是不会重复的,而转换重心对答案不会有任何贡献,因为 A B AB AB余数集独立,数据范围只有 100 100 100,所以直接已二者都为重心跑一遍,取小的即可,详情见代码。
#include<iostream>
#include<queue>
#include<cmath>
#include<algorithm>
#define int long long
using namespace std;
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int a,b,c;
cin>>a>>b>>c;
if(c%__gcd(a,b)!=0)
{
cout<<"impossible";
return 0;
}
int x=0,y=0;
queue<int>q,q2;
int id=0,id2=0;
while(x!=c&&y!=c&&id<=100000)
{
id++;
if(y==b)
{
q.push(3);
y=0;
}
else if(x)
{
q.push(2);
int z=x+y;
y=min(b,y+x);
x=max(0ll,z-b);
}
else
{
q.push(1);
x=a;
}
}
x=0,y=0;
swap(a,b);
while(x!=c&&y!=c&&id2<=100000)
{
id2++;
if(y==b)
{
q2.push(3);
y=0;
}
else if(x)
{
q2.push(2);
int z=x+y;
y=min(b,y+x);
x=max(0ll,z-b);
}
else
{
q2.push(1);
x=a;
}
}
if(id<id2)
{
cout<<id<<"\n";
while(q.size())
{
int z=q.front();
q.pop();
if(z==1)cout<<"FILL(1)\n";
else if(z==2)cout<<"POUR(1,2)\n";
else cout<<"DROP(2)\n";
}
}
else
{
cout<<id2<<"\n";
while(q2.size())
{
int z=q2.front();
q2.pop();
if(z==1)cout<<"FILL(2)\n";
else if(z==2)cout<<"POUR(2,1)\n";
else cout<<"DROP(1)\n";
}
}
return 0;
}