题意
给你a,b,c代表三个杯子的容量,刚开始的时候只有c的杯子是满水的,现在给你一个d,问你将水倒来倒去能不能使得一个杯子的水的容量是d,并且要求该过程中倒水的总量最小,如果不存在,输出一个最靠近d的数字
思路
我们考虑两个杯子a,b倒水的过程,只存在两者情况,1要么将b杯子的水全部倒满,2将a杯子的水全部倒过去使得a杯子空了但是b没有满,我们在这样的倒水的过程中肯定是取两者的最小值,我们定义ans[i] 为出现的水杯容量为i时的全部过程中倒的水的总容量,然后用优先队列维护bfs过程,每次都是从队列中取水的最小值。至于为什么判重不能用i,j而是用0,1,其实想想就知道i,j表示的意思就不对。
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
struct node{
int val[3],d;
bool operator <(const struct node & b)const{
return d>b.d;
}
};
const int MAXN=205;
int vis[MAXN][MAXN];
int ans[MAXN];
void bfs(int a,int b,int c,int d)
{
int st[3]={a,b,c};
node t;
priority_queue<node> pq;
t.d=0;
t.val[0]=0;
t.val[1]=0;
t.val[2]=c;
pq.push(t);
memset(vis,0,sizeof(vis));
memset(ans,-1,sizeof(ans));
vis[0][0]=1;
while(!pq.empty())
{
node k=pq.top();pq.pop();
for(int i=0;i<3;i++)
{
int kk=k.val[i];
if(ans[kk]<0||ans[kk]>k.d) ans[kk]=k.d;
}
if(ans[d]>=0) break;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
node kk;
memcpy(&kk,&k,sizeof(k));
if(i==j||!k.val[i]||k.val[j]==st[j]) continue;
int tt=min(st[j],k.val[i]+k.val[j])-k.val[j];
kk.val[i]-=tt;
kk.val[j]+=tt;
kk.d+=tt;
if(!vis[kk.val[0]][kk.val[1]])
{
vis[kk.val[0]][kk.val[1]]=1;
pq.push(kk);
}
}
}
}
while(d>=0)
{
if(ans[d]>=0)
{
printf("%d %d\n",ans[d],d);
break;
}
d--;
}
}
int main()
{
int a,b,c,d;
int T;cin>>T;
while(T--)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
bfs(a,b,c,d);
}
return 0;
}