如家大神书上的例题。第一次接触也是按代码敲得。敲的过程感觉很直观。但自己写估计会写的乱七八糟。以后不能砍得难就不愿意做这种题。否则只能做一些水题了。(PS:48)
紫书
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<queue> 6 using namespace std; 7 const int maxn = 205; 8 struct Node 9 { 10 int v[3]; 11 int dist; //总倒水量 12 bool operator < (const Node& a) const 13 { 14 return dist > a.dist; 15 } 16 }; 17 int vis[maxn][maxn], ans[maxn]; 18 int cap[3]; 19 void update_ans(const Node& u) 20 { 21 for(int i = 0; i < 3; i++) 22 { 23 int d = u.v[i]; //当前状态下每个杯的水量; 24 if(ans[d] < 0 || u.dist < ans[d]) 25 ans[d] = u.dist; 26 } 27 } 28 void solve(int a, int b, int c, int d) 29 { 30 cap[0] = a; cap[1] = b; cap[2] = c; 31 memset(vis, 0, sizeof(vis)); 32 memset(ans, -1, sizeof(ans)); 33 priority_queue<Node> q; //优先队列应用! 34 35 Node start; 36 start.v[0] = 0; start.v[1] = 0; start.v[2] = c; start.dist = 0; 37 q.push(start); 38 39 vis[0][0] = 1; 40 while(!q.empty()) 41 { 42 Node u = q.top(); q.pop(); //选择倒水量少的扩展 43 update_ans(u); 44 if(ans[d] >= 0) break; 45 for(int i = 0; i < 3; i++) 46 for(int j = 0; j < 3; j++) 47 {//把水从i倒入j 48 if(i == j) continue; 49 if(u.v[i] == 0 || u.v[j] == cap[j]) continue; //i没水或j满 50 int amount = min(cap[j], u.v[i]+u.v[j]) - u.v[j]; //从i倒入j中水的量 51 Node v; 52 memcpy(&v, &u, sizeof(u)); 53 v.v[i] -= amount; v.v[j] += amount; v.dist += amount; 54 if(!vis[v.v[0]][v.v[1]]) //状态记忆;因总水量一定,故只知道其中两个杯子的水量就可知当前状态。故记忆数组用二维即可 55 { 56 vis[v.v[0]][v.v[1]] = 1; 57 q.push(v); 58 } 59 } 60 } 61 while(d >= 0) //若达不到,则尽可能接近 62 { 63 if(ans[d] >= 0) 64 { 65 printf("%d %d ", ans[d], d); 66 return ; 67 } 68 d--; 69 } 70 } 71 int main() 72 { 73 int T; scanf("%d", &T); 74 while(T--) 75 { 76 int a, b, c, n; 77 scanf("%d%d%d%d", &a, &b, &c, &n); 78 solve(a, b, c, n); 79 } 80 return 0; 81 }
P205.直接上代码