您的位置:首页 > 其它

uva 10603(Fill, 隐式图搜索问题)

2015-09-06 20:48 225 查看
题目大意:

有三个杯子的容量分别为a, b, c, 最初只有第3个杯子装满了c升水,其他两个杯子为空。最少需要倒多少升水才能让某个水杯有d升,如果无法达到d升,找到某个杯子的水是d’升,其中d‘<d并且尽量接近d。(1< = a, b, c, d <= 200)。 要求输出最少的倒水量和目标水量(d或者d')。

题目分析:

首先,将三个杯子的水量看成是状态(v0, v1, v2); 那么我们可以描述出倒水过程中的状态变化:

这里我用结构体node 表示这个状态:

struct Node{
int w[3], dist;  // w[i]表示第i个水杯中的水量; (i是从0开始计数) , dist表示目前为止最少的倒水量;
};


那么怎么表示状态的变化呢;

</pre><pre name="code" class="cpp">//尝试将第i杯子中的水倒入j杯子中;
Node v = u; // u为之前状态;
for i = 0 to 2
for(j = 0 to 2)
if(j == i) continue;  //不能自己给自己倒水;
int water = min(v.w[i], cap[j]-v.w[j]);  //cap[i]表示i杯子的容积; water表示可以倒的水量;
v.w[i] -= water;
v.w[j] += water;


有了前面的介绍我们回到问题中,怎么求最少的倒水量和目标水量呢?

我们从起始的状态开始,枚举所有可能变化的状态,进行保存记录,不就可以了;

每个杯子的水量最多有201种可能,那么所有的状态不会超过201*201 = 40401; 是可以进行遍历的;

代码如下:

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>

using namespace std;

const int maxn = 200+5;
int cap[3], d;
struct Node{
int w[3], dist;
};
int ans[maxn], vis[maxn][maxn];

void Init(){
memset(vis, 0, sizeof(vis));
memset(ans, -1, sizeof(ans));
}

void update_ans(Node &s){
for(int i = 0; i < 3; ++i){
int d = s.w[i];
if(ans[d] < 0 || s.dist < ans[d]) ans[d] = s.dist;
}
}

void bfs(){
queue<Node> q;
Node u, v;
u.w[0] = u.w[1] = 0; u.w[2] = cap[2];
u.dist = 0;
vis[0][0] = 1;
q.push(u);
while(!q.empty()){
u = q.front();
q.pop();
update_ans(u);
for(int i = 0; i < 3; ++i)
for(int j = 0; j < 3; ++j){
if(i == j) continue;
v = u;
int water = min(u.w[i], cap[j]-u.w[j]);
v.w[i] -= water;
v.w[j] += water;
v.dist += water;
int ok = 0;
for(int i = 0; i < 3; ++i){   // 一定注意这里, 不是访问过就不能访问了,如果倒水总量小于之前的,依旧要访问;
if(v.dist < ans[v.w[i]])
ok = 1;
}
if(!vis[v.w[0]][v.w[1]] || ok){
vis[v.w[0]][v.w[1]] = 1;
q.push(v);
}
}
}
}

int main()
{
int T;
scanf("%d", &T);
while(T--){
scanf("%d%d%d%d", &cap[0], &cap[1], &cap[2], &d);
Init();
bfs();
for(int i = d; i >= 0; --i)
if(ans[i] >= 0){    //之前写成>找了很长时间的bug, 注意这些细节;
printf("%d %d\n", ans[i], i);
break;
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: