题意:
设三个没有刻度杯子的容量为a, b, c, 刚开始只有第三个杯子装满水,其他两个 杯子为空,问最少需要倒多少升水才能使其中一个杯子中有 d 升水,若无法做到恰好 d 升,就让某一个杯子的水是 d' 升,其中 d' < d 并且尽量接近d,
注意:由于杯子没有刻度,用杯子x给杯子y倒水时,要么把x倒空,要么把y倒满。。
分析:
该题可看成图的最短路问题,把三个杯子的状态(三元组)看成图中的节点,两个状态倒水量看成边,(边的大小不同,不能用bfs)然后用Dijkstra算法求解即可。。
代码实现:
借鉴了紫书上的漂亮代码(自己写了180多的代码,结果错的莫名其妙)
由于水的总量是固定的,所以根据前两个杯子的水量就可以确定一个状态,vis数组开个二维就行了
as数组存三个杯子水量,dis表示从初始状态到达此时状态所需的倒水量
ans[s]表示其中一个杯子的水量为s时的所需的最小倒水量
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct State{
ll as[3],dis=0;
State(ll a,ll b,ll c){
as[0]=a,as[1]=b,as[2]=c;
}
bool operator>(State s2) const{
return dis>s2.dis;
}
};
bool vis[250][250];
ll ans[250];
priority_queue<State,vector<State>,greater<State>>q;
ll cap[250];
void update_ans(const State &u){
ll s;
for(ll i=0;i<3;i++){
s=u.as[i];
if(ans[s]<0||u.dis<ans[s]) ans[s]=u.dis;
}
}
void solve(ll a,ll b,ll c,ll d){
while(!q.empty()){
State u=q.top();q.pop();
update_ans(u);
if(vis[u.as[0]][u.as[1]]) continue;
if(ans[d]>=0) return;
vis[u.as[0]][u.as[1]]=1;
for(ll i=0;i<3;i++){ //i是倒水的杯子,j是被倒的
for(ll j=0;j<3;j++){
State tmp=u;
if(i==j||u.as[i]==0||u.as[j]==cap[j]) continue;
ll amount=min(cap[j],u.as[i]+u.as[j])-u.as[j]; //倒水量 ,j倒完后的水量-倒之前的水量
u.as[i]-=amount;
u.as[j]+=amount;
u.dis+=amount;
if(!vis[u.as[0]][u.as[1]])
q.push(u);
u=tmp;
}
}
}
}
int main()
{
ll t,a,b,c,d;
cin>>t;
while(t--){
while(!q.empty()) q.pop();
memset(vis,0,sizeof(vis));
memset(ans,-1,sizeof(ans));
cin>>a>>b>>c>>d;
cap[0]=a,cap[1]=b,cap[2]=c;
q.push(State(0,0,c));
solve(a,b,c,d);
for(ll s=d;s>=0;s--){
if(ans[s]>=0){
cout<<ans[s]<<" "<<s<<endl;
break;
}
}
}
return 0;
}
ps:
第一次写博客,我尽力了...