简单搜索的不搜索解法(Pots POJ 3414)

简单搜索的不搜索解法(Pots POJ 3414)

题意:

给定两个容量分别为 A 升和 B 升的罐子。可以执行以下操作:

  1. F I L L ( i ) FILL(i) FILL(i) 从水龙头向罐子 i ( 1 ≤ i ≤ 2 ) i (1 ≤ i ≤ 2) i(1i2) 倒水;

  2. D R O P ( i ) DROP(i) DROP(i) 把罐子 i 的水倒掉;

  3. 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) Cmax(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=3B=4 时,C等于任何数均有合法解。因为 C % B = X ( X ∈ { 0 , 1 , 2 , 3 } ) C\%B=X(X∈\{0,1,2,3\}) C%B=X(X{0123}) ,而 n ∗ A % B n*A\%B nA%B 一定可以可以凑出 C C C 缺的那个余数。

进一步考虑什么时候不行,当余数缺的时候一定不行,例如 2 , 4 2,4 24一定凑不出 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;
}

  • 26
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值