Pots
Description You are given two pots, having the volume of A and B liters respectively. The following operations can be performed:
Write a program to find the shortest possible sequence of these operations that will yield exactly C liters of water in one of the pots. Input On the first and only line are the numbers A, B, and C. These are all integers in the range from 1 to 100 and C≤max(A,B). Output The first line of the output must contain the length of the sequence of operations K. The following K lines must each describe one operation. If there are several sequences of minimal length, output any one of them. If the desired result can’t be achieved, the first and only line of the file must contain the word ‘impossible’. Sample Input 3 5 4 Sample Output 6 FILL(2) POUR(2,1) DROP(1) POUR(2,1) FILL(2) POUR(2,1) Source
Northeastern Europe 2002, Western Subregion
|
special judge 我的理解是,有多个答案吧,你只需要出来一个正确的答案就算正确,
题意大致就是给你两个杯子和一个你需要达到的水的升数,不妨,我就假设为三杯水,前两个为容器,第三个已经给你了,让你用前两个相互倒水,达到第三个杯子的水量(不知道这么说是复杂了还是简单了)
一看这个题,觉得是暴力搜索,然而剪枝不怎么会,去网上扒拉了一下他们的代码,发现有个很简单的方法我在这里说说大体意思:如果两个杯子互为倍数关系,而且打三个杯子和前两个不是倍数关系,那么就一定达不到。对于一般的情况,无外乎从第一个杯子倒入第二个杯子,如果第二个杯子满了,那么就清空第二个杯子,如果第一个杯子空了,就倒满第一个杯子,那么最后一定会出来结果;或者是从第二个杯子倒入第一个杯子,如果第一个杯子满了,就清空第一个杯子,如果第二个杯子空了,就加满他,一直循环下去。这两个方式中一定有一个是最好的,里面会用到sscanf()函数,不懂得可以去看看这个博客http://blog.csdn.NET/i1020/article/details/53557116, 我们在这里用的也是很简单的,不看也差不多。
这里附上ac代码
#include <stdio.h>
char str1[5000][20],str2[5000][20];
int main()
{
int a,b,c,cnt1,cnt2,i,j;
while(~scanf("%d%d%d",&a,&b,&c))
{
int t1 = a > b ? a : b;
int t2 = a + b - t1;
cnt1 = 0;
if(t1 % t2 == 0 && c % t2 !=0)//判断是否能够产生c升
printf("impossible\n");
else
{
i=0;
j=0;//i代表第二个杯子,j代表第一个杯子
while(i!=c&&j!=c)
{
if(!i)
{//如果第二个杯子是空的,就给他倒水
i=b;
sscanf("FILL(2)\n","%s",str1[cnt1++]);
}
else if(j == a)
{//如果第一个杯子满了,就倒掉
j=0;
sscanf("DROP(1)\n","%s",str1[cnt1++]);
}
else
{//所有情况上面都判断过了,然后开始倒水
int tmp=i;//备份一下现在的水量
i=i-(a-j);//i编程倒完后的剩余量
sscanf("POUR(2,1)\n","%s",str1[cnt1++]);
if(i<0)//这时候是第一个杯子太大,
i = 0;
j=j + tmp;//倒进来
if(j > a)//第一个杯子放不下了
j=a;
}
//这只是走了一步,每一步都回到while循环,每一步都判断是否达到额定的水量
}
//现在开始第二种走法
i=0;
j=0;
cnt2=0;
while(i!=c&&j!=c)
{
if(!j)
{//如果第一个杯子是空的,就给他倒水
j=a;
sscanf("FILL(1)\n","%s",str2[cnt2++]);
}
else if(i == b)
{//如果第二个杯子满了,就倒掉
i=0;
sscanf("DROP(2)\n","%s",str2[cnt2++]);
}
else
{//所有情况上面都判断过了,然后开始倒水
int tmp=j;//备份一下现在的水量
j=j-(b-i);//i编程倒完后的剩余量
sscanf("POUR(1,2)\n","%s",str2[cnt2++]);
if(j<0)//这时候是第二个杯子太大,
j = 0;
i=i + tmp;//倒进来
if(i > b)//第二个杯子放不下这么多水
i=b;
}
//这只是走了一步,每一步都回到while循环,每一步都判断是否达到额定的水量
}
printf("%d\n",cnt1 < cnt2 ? cnt1 : cnt2);//选择最小的输出
if(cnt1 > cnt2)
{
for(i = 0; i < cnt2; i ++)
printf("%s\n",str2[i]);
}
else
{
for(i = 0; i < cnt1; i ++)
printf("%s\n",str1[i]);
}
}
}
return 0;
}
第二种方法就是用bfs搜索,因为bfs的特点就是出来最优解
我简单说一下模型,总共6种转移:清空1,清空2,满1,满2,1到2,2到1。bfs遍历一遍就行,
这6种操作在我的程序中分别用一个整数进行记录;
1z0: 清空z瓶子
2z0: 装满z瓶子
3xy: 从x瓶倒向y瓶
(x,y,z∈{1,2})
这样在输出操作时就能根据数字的特点 进行选择性输出 了
输出用的递归输出ac代码:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#define INF 110
using namespace std;
int a,b,c;//容器大小和要达到的容量值
int vis[INF][INF];//标记数组,记录一个个状态是否发生过
struct node{
int k1,k2;//当前水的状态
int dd;//当前进行的操作
int pre;//记录他的父节点
int step;//记录步数
}nn[12105];//其实INF*INF就可
int dir[6]={110,120,210,220,312,321};//分别代表六种状态
void output(node t)
{
if(t.pre!=-1)
{
output(nn[t.pre]);
if(t.dd==110)
{
printf("FILL(1)\n");
}
else if(t.dd==120)
{
printf("FILL(2)\n");
}
else if(t.dd==210)
{
printf("DROP(1)\n");
}
else if(t.dd==220)
{
printf("DROP(2)\n");
}
else if(t.dd==312)
{
printf("POUR(1,2)\n");
}
else if(t.dd==321)
{
printf("POUR(2,1)\n");
}
}
else
return ;
}
int bfs()
{
int i,j,falg=0,front,rear;
nn[0].k1=0;
nn[0].k2=0;
nn[0].dd=0;
nn[0].step=0;
nn[0].pre=-1;//代表无前驱
front=0;
rear=1;
memset(vis,0,sizeof(vis));
vis[0][0]=1;//初始状态已经发生过
while( front < rear )//没走一遍
{
if(nn[front].k1==c || nn[front].k2==c)
{//应该不会出现这种情况,既c=0;
printf("0\n");
return 1;
}
for(i=0;i<6;i++)
{//六种状态全部进行一遍
node tmp;
if(dir[i]==110)
{
tmp.k1=a;
tmp.k2=nn[front].k2;
}
else if(dir[i]==120)
{
tmp.k2=b;
tmp.k1=nn[front].k1;
}
else if(dir[i]==210)
{
tmp.k1=0;
tmp.k2=nn[front].k2;
}
else if(dir[i]==220)
{
tmp.k2=0;
tmp.k1=nn[front].k1;
}
else if(dir[i]==312)
{
if(nn[front].k1+nn[front].k2 < b)
{
tmp.k2=nn[front].k1+nn[front].k2;
tmp.k1=0;
}
else
{
tmp.k2=b;
tmp.k1=nn[front].k1+nn[front].k2-b;
}
}
else if(dir[i]==321)
{
if(nn[front].k1+nn[front].k2 < a)
{
tmp.k1=nn[front].k1+nn[front].k2;
tmp.k2=0;
}
else
{
tmp.k1=a;
tmp.k2=nn[front].k1+nn[front].k2-a;
}
}
tmp.dd=dir[i];
tmp.pre=front;
tmp.step=nn[front].step+1;
if(!vis[tmp.k1][tmp.k2])//当前状态未经历过
{
vis[tmp.k1][tmp.k2]=1;
nn[rear]=tmp;
rear++;
if(tmp.k1==c||tmp.k2==c)
{
printf("%d\n",tmp.step);
output(nn[rear-1]);
return 1;
}
}
}
front++;
}
return 0;
}
int main(){
int flag;
while(~scanf("%d%d%d",&a,&b,&c))
{
flag=0;
flag=bfs();
if(!flag)
{
printf("impossible\n");
}
}
return 0;
}