/**********************************************************
2017.1.24
重新做了一遍这个题目。
三个杯子,总水量是一样的,只需要记录前两个杯子来作为状态来判重即可。
然后他要求总到水量最小,直接贪心就好了,优先选取总水量最小的,用优先队列即可。
然后找一个最接近d的答案即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
struct Node{
int v[3];
int val;
Node(){
val = 0;
memset(v,0,sizeof v);
}
bool operator < (const Node& rhs) const {
return val > rhs.val;
}
}st;
priority_queue<Node> q;
bool vis[207][207];
int all;
int ans1,ans2;
int a,b,c,d;
int va[5];
void bfs(){
while(!q.empty())q.pop();
memset(vis,0,sizeof vis);
q.push(st);
vis[st.v[0] ][st.v[1]] = 1;
while(!q.empty()){
Node u = q.top(); q.pop();
for (int i = 0; i < 3; ++i){
if (u.v[i] <= d){
if (u.v[i] > ans1){
ans1 = u.v[i];
ans2 = u.val;
if (ans1 == d) return ;
}
}
}
for (int i = 0; i < 3; ++i){
for (int j = 0; j < 3; ++j){ ///i -> j;
if (i != j && u.v[j] != va[j] && u.v[i] != 0){
int lef = va[j] - u.v[j];
int hav = u.v[i];
Node ne = Node();
for (int k = 0; k < 3; ++k) ne.v[k] = u.v[k];
if (hav >= lef){
ne.v[i] = hav - lef;
ne.v[j] = va[j];
ne.val = u.val + lef;
}
else {
ne.v[i] = 0;
ne.v[j] += hav;
ne.val = u.val + hav;
}
if (!vis[ne.v[0]][ne.v[1]]){
vis[ne.v[0]][ne.v[1]] = 1;
q.push(ne);
}
}
}
}
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
ans1 = -0x3f3f3f3f;
st = Node();
scanf("%d %d %d %d",&a, &b, &c, &d);
va[0] = a; va[1] = b; va[2] = c;
all = a + b + c;
st.v[0] = 0; st.v[1] = 0; st.v[2] = c; st.val = 0;
bfs();
printf("%d %d\n",ans2,ans1);
}
return 0;
}
完全仿照紫书上来写的!
书中大体思路是:
用ans[]来记录答案,不断取最小值来更新!
用vis[][]来表示是否访问过,之所以是二维数组,是因为总水量是固定的,两个杯子确定,第三个杯子自然也就确定,两个状态足矣!
用结构体表示每一个状态!其中包括每个杯子的水量!u.wat[],还有dist 为到目前这个状态总的取水量!
然后用优先队列不断倒水,
有一个技巧,代码中先算出需要倒的水量,然后再计算!这样结果不会出现负值,两个情况都考虑到了!很巧妙!
int m = min(cup[j],u.wat[i]+u.wat[j]) - u.wat[j];//m 为需要倒水的量,这样写 不会出现负值!
最后注意结果可能会有0就行了!
#include<bits/stdc++.h>
using namespace std;
struct Node{
int wat[3],dist;//wat[]是各个杯子里的水,dist是总取水量!
bool operator < (const Node &rhs) const {
return dist > rhs.dist;//最小值优先级大!
}
};
const int maxn = 200 + 10;
int cup[3],vis[maxn][maxn],ans[maxn];//vis[][]之所以是二维数组,是因为总水量是固定的,两个杯子确定,第三个杯子自然也就确定,两个状态足矣!
void update_ans(const Node &u){
for (int i = 0; i < 3; ++i){
int d = u.wat[i];
if (ans[d] < 0 || u.dist < ans[d])ans[d] = u.dist;//在优先队列中每提出一个队首元素,就对应一个新的状态,就需要在ans[]更新状态!更新取水量最小的状态
}
}
void solve(int a,int b,int c,int d){
memset(ans,-1,sizeof(ans));
memset(vis,0,sizeof(vis));
cup[0] = a; cup[1] = b; cup[2] = c;//初始化杯子的容量!
priority_queue<Node>q;
Node u;
u.wat[0] = u.wat[1] = 0; u.wat[2] = c;
u.dist = 0;
q.push(u);
while(!q.empty()){
u = q.top(); q.pop();
update_ans(u);
if (ans[d] >= 0)break;
for (int i = 0; i < 3; ++i)//i倒入j
for (int j = 0; j < 3; ++j)
if (i != j){
if (cup[i] <= 0 || u.wat[j] == cup[j])continue;
int m = min(cup[j],u.wat[i]+u.wat[j]) - u.wat[j];//m 为需要倒水的量,这样写 不会出现负值!
Node v = u;
v.dist += m;
v.wat[i] -= m;
v.wat[j] += m;
int key1 = v.wat[0],key2 = v.wat[1];//key1,key2为前两个杯子的状态!
if (vis[key1][key2] == 0){//未访问过这个状态!
vis[key1][key2] = 1;
q.push(v);
}
}
}
while(d >= 0){//注意有等号,结果可能会有0,就是一开始就符合!
if (ans[d] >= 0){printf("%d %d\n",ans[d],d);return;}
--d;
}
}
int main()
{
int T,a,b,c,d;
scanf("%d",&T);
while(T--){
scanf("%d%d%d%d",&a,&b,&c,&d);
solve(a,b,c,d);
}
return 0;
}