题解:【PA2021】Butelki

题目链接

设三个瓶子分别为 \(x\)\(y\)\(z\),考虑一次操作只会有一个瓶子倒向另一个瓶子,所以最多延伸出六个新状态:\(x \to y\)\(x \to z\)\(y \to x\)\(y \to z\)\(z \to x\)\(z \to y\)。每种状态都有两种可能,一个是倒空了当前的瓶子,另一个是倒满了要倒的瓶子。

不妨记三个瓶子初始的水量为 \(a,b,c\),一次操作过后,必定有一个瓶子是满的或者空的,钦定 \(a\) 是满的,那么剩下两个瓶子的状态数便是这三种情况之一:

\[\min\{b,c\} + \begin{cases} \min\{a,b\} + \min\{a,c\} \\ \min\{a,b\} + 1 \\ \min\{a,c\} + 1 \end{cases} \]

于是得到了一个 \(2a + 2b + 2c +2 \times 3 + 1\) 数量的状态上界,其中 \(2 \times 3\) 为六种不同倒的情况,\(1\) 为初始状态,足以证明是状态数是线性增长的。因此尽管数据范围有 \(10^5\),依然可以直接记忆化搜索。

记录只需要记三个瓶子的容量和当前状态的步数,直接压到一个数里面就好,这样也方便判重。

其他细节直接看代码罢。

\(Code\)

#include<bits/stdc++.h>
#define int long long
using namespace std;

inline int read()
{
	int s=0,w=1;
	char c=getchar();
	while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	while(isdigit(c)) s=(s<<1)+(s<<3)+(c^48),c=getchar();
	return s*w;
}

namespace LgxTpre
{
	static const int MAX=100010;
	static const int INF=4557430888798830399;
	static const int mod=998244353;
	static const int puz=1000000;
	
	int x_start,y_start,z_start;
	int ans[MAX];
	int up[4],bot[4],tmp[4],goal;
	
	queue<pair<int,int> > q;
	map<int,bool> mp;
	inline int get(int x,int y,int z)
	{
		return x*1000000000000+y*1000000+z;
	}
	inline void pullin(int tim)
	{
		int now=get(tmp[1],tmp[2],tmp[3]);
		if(mp.find(now)!=mp.end()) return;
		q.push(make_pair(now,tim));
		mp[now]=1;
		return;
	}
	
	inline void lmy_forever()
	{
		memset(ans,0x3f,sizeof ans);
		up[1]=read(),up[2]=read(),up[3]=read(); goal=up[3];
		x_start=read(),y_start=read(),z_start=read();
		q.push(make_pair(get(x_start,y_start,z_start),0));
		while(!q.empty())
		{
			int now=q.front().first,tim=q.front().second; q.pop();
			bot[3]=now%puz,now/=puz,ans[bot[3]]=min(ans[bot[3]],tim);
			bot[2]=now%puz,now/=puz,ans[bot[2]]=min(ans[bot[2]],tim);
			bot[1]=now%puz,now/=puz,ans[bot[1]]=min(ans[bot[1]],tim);
			for(int i=1;i<=3;++i)
				for(int j=1;j<=3;++j)
				{
					if(i==j) continue;
					int sum=bot[i]+bot[j];
					memcpy(tmp,bot,sizeof bot);
					if(sum>=up[j]) tmp[i]=sum-up[j],tmp[j]=up[j],pullin(tim+1);
					else tmp[i]=0,tmp[j]=sum,pullin(tim+1);
				}
		}
		for(int i=0;i<=goal;++i)
			cout<<(ans[i]==INF?-1:ans[i])<<" ";
		return;
	}
}

signed main()
{
	LgxTpre::lmy_forever();
	return (0-0);
}
posted @ 2023-02-07 16:54  LgxTpre  阅读(30)  评论(0编辑  收藏  举报