看了书上的解法,自己写了一遍,几乎没遇到什么障碍。不过看Board上面的讨论,觉得细节上需要注意的地方还是挺多的。。纯粹自己写肯定分分钟跪下
1、vis数组只用开两维
看到讨论中大家都是用的三维数组vis[201][201][201],其实有了两杯水的数据,第三杯水可以用总量减去两杯水的量得到,所以不需要维护3维数组
2、解的存储
因为题目说如果取不到d,就要打印出比d小的最大的可能解d'以及倒水的量,所以bfs就把所有可能的d以及倒水量都存了下来,如果之前取到过这个d,则判断倒水量是否比之前的少,如果满足要求的话就更新数据。最后从d开始往下减小,遇到第一个取到过的解就打印并返回
3、优先队列
首先要注意这道题问的是倒水量最少,而不是倒水次数最少,而这两者没有必然关系。所以在bfs的时候,就不能用普通queue存储需要遍历的节点,而应该用一个优先队列,让倒水量少的排在前头。
另外在讨论中看到很多人用的是dfs,并且很多人得了TLE。然后大家比较推荐的解法是dijkstra。
Run Time: 0.142s
#define UVa "LT7-8.10603.cpp"
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<queue>
#define maxn 500
using namespace std;
struct Node {
int v[3], dist;
bool operator < (const Node& n) const {
return dist > n.dist;
}
};
//Global Variables.
int vis[maxn][maxn];
int ans[maxn];
/
void update_ans(Node& u) {
for(int i = 0; i < 3; i ++) {
int d = u.v[i];
if(ans[d] < 0 || u.dist < ans[d]) ans[d] = u.dist;
}
}
void solve(int a, int b, int c, int d) {
int cap[3] = {a,b,c};
memset(vis, 0, sizeof(vis));
memset(ans, -1, sizeof(ans));
Node startN;
startN.v[0] = startN.v[1] = 0;
startN.v[2] = c;
startN.dist = 0;
priority_queue<Node> q;
q.push(startN);
vis[0][0] = 1;
while(!q.empty()) {
Node u = q.top(); q.pop();
update_ans(u);
for(int i = 0; i < 3; i ++) {
for(int j = 0; j < 3; j ++) if(i != j) {
if(u.v[j] == cap[j] || u.v[i] == 0) continue; //Empty / Full
int amount = min(cap[j] - u.v[j], u.v[i]); //Amount of water to fill
Node newN;
memcpy(&newN, &u, sizeof(u));
newN.v[i] = u.v[i] - amount;
newN.v[j] = u.v[j] + amount;
newN.dist = u.dist + amount;
if(!vis[newN.v[0]][newN.v[1]]) {
q.push(newN);
vis[newN.v[0]][newN.v[1]] = 1;
}
}
}
}
while(d>=0) { //print ans
if(ans[d] >= 0) {
printf("%d %d\n", ans[d], d);
return;
}
d--;
}
}
int main() {
int kase;
scanf("%d", &kase);
while(kase--) {
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
solve(a, b, c, d);
}
return 0;
}