Fill-倒水问题(Uva-10603-隐式图路径寻找问题)

原题:https://uva.onlinejudge.org/external/106/10603.pdf


有三个没有刻度的杯子,它们的容量分别是a, b, c, 最初只有c中的杯子装满水,其他的被子都是空的。问如果通过倒水得到d升水, 若可以得到,还要求最少的倒水总量(每倒一次水,都加入到总量里)。若不能得到d升水,求比d小的距离d最近的d‘升水, 及最少倒水总量。


分析:

因为是求最少倒水总量,本能地想到用bfs搜索。最开始读错题了...看成求倒水的最少次数,这个很简单.....我们可以把求解的过程看成是空间状态的搜索,每一个状态都有一组a, b, c, pour_amount (分别对应三个杯子里的水量和到达当前状态需要的倒水总量)。如果把每个状态想象成一个结点, 整个搜索的过程就是图的遍历过程。


难点:

这道题的每一个状态有四个变量,所以要考虑所有四种变量的所有变化情况。方法是每次枚举当前a,b,c的下一个可达结点。这里有一个可以回溯剪枝的要点,就是结点判重,如果下一个结点的a,b,c曾经遍历过且它的pour_amount小于当前的pour_amount,回溯.结点判重的时候还有一个难点就是自己实现一个哈希表.对于一个状态a,b,c,对应一个到达这个状态的最小pour_amount

如果用stl自带的map,速度非常慢,比如1000组数据需要耗时4s左右

然而如果用自己实现的hash_map,1000组数据只要0.003s

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <queue>
 6 using namespace std;
 7
 8 const int MAXHASHSIZE = 500;
 9 int d, _head[MAXHASHSIZE], _next[MAXHASHSIZE];
10 int vol[3], _d, ans, idx, st[MAXHASHSIZE], pour_set[MAXHASHSIZE];
11
12 struct Node {
13     int v[3], pour;
14     Node(int a = 0, int b = 0, int c = 0, int p = 0) {
15         v[0] = a; v[1] = b; v[2] = c; pour = p;
16     }
17     bool operator == (const Node &rhs) const {
18         for (int i = 0; i < 3; i++) if (rhs.v[i] != v[i]) return false;
19         if (rhs.pour != pour) return false;
20         return true;
21     }
22 };
23
24 void init() {
25     _d = -1;
26     ans = 2147483647;
27     idx = 1;
28     memset(_head, 0, sizeof(_head));
29 }
30
31 bool try_to_insert(Node &cur) {
32     int status = cur.v[0] * 1000000 + cur.v[1] * 1000 + cur.v[2];
33     int h = status % MAXHASHSIZE;
34     int u = _head[h];
35     while (u) {
36         if (st[u] == status) {
37             if (cur.pour < pour_set[u]) {
38                 pour_set[u] = cur.pour;
39                 return 1;
40             }
41             return 0;
42         }
43         u = _next[u];
44     }
45     st[idx] = status;
46     pour_set[idx] = cur.pour;
47     _next[idx] = _head[h];
48     _head[h] = idx++;
49     return 1;
50 }
51
52 void bfs(Node start) {
53     pour_set[1] = start.pour;
54     try_to_insert(start);
55     queue<Node> q;
56     q.push(start);
57     while (!q.empty()) {
58         Node cur = q.front(); q.pop();
59         for (int i = 0; i < 3; i++) {
60             if (cur.v[i] == _d)
61                 ans = min(ans, cur.pour);
62             else if (cur.v[i] > _d && cur.v[i] <= d) {
63                 ans = cur.pour;
64                 _d = cur.v[i];
65             }
66             if (cur.v[i] != 0)
67                 for (int j = 0; j < 3; j++) if (i != j) {
68                     int pour = min(cur.v[i], vol[j] - cur.v[j]);
69                     cur.v[i] -= pour;
70                     cur.v[j] += pour;
71                     Node nextn = Node(cur.v[0], cur.v[1], cur.v[2], cur.pour + pour);
72                     cur.v[i] += pour;
73                     cur.v[j] -= pour;
74                     if (try_to_insert(nextn)) q.push(nextn);
75                 }
76         }
77     }
78 }
79
80 int main() {
81     int T;
82     scanf("%d", &T);
83     while (T--) {
84         init();
85         scanf("%d%d%d%d", &vol[0], &vol[1], &vol[2], &d);
86         bfs(Node(0, 0, vol[2], 0));
87         printf("%d %d\n", ans, _d);
88     }
89     return 0;
90 }


Fill-倒水问题(Uva-10603-隐式图路径寻找问题)

时间: 2024-10-19 07:21:56

Fill-倒水问题(Uva-10603-隐式图路径寻找问题)的相关文章

倒水问题 (FillUVa 10603) 隐式图

题意:本题的题意是给你三个杯子,第一二个杯子是空的,第三个杯子装满水,要求是量出一定容量d升的水.若是得不到d升的水,那就让某一个杯子里面的水达到d',使得d'尽量接近d升. 解题思路:本题是给出初始状态,让你寻找一条通往目标的路径,此题也可看成是有向图中在起点和目标点之间寻找一条最短路径,但是这个最短路径不是距离最短而是倒的水最少,所以这题类似于Dijkstra算法求最短路,利用广搜,一直选当前水量最少的结点进行扩展,所以可以建立一个优先队列来存储下一次要访问的结点,同时将访问过的结点标记一下

【UVA】658 - It&#39;s not a Bug, it&#39;s a Feature!(隐式图 + 位运算)

这题直接隐式图 + 位运算暴力搜出来的,2.5s险过,不是正法,做完这题做的最大收获就是学会了一些位运算的处理方式. 1.将s中二进制第k位变成0的处理方式: s = s & (~(1 << pos)); 将s中二进制第k位变成1的处理方式: s = s | (1 << pos); 2.二进制运算: [1] & : 1 & 1 = 1 , 1 & 0 = 0 , 0 & 0 = 0; 快速判断奇偶性: if(a & 1);//为奇数

隐式图的遍历

好久不看都快忘了... 一些奇怪的问题可以归为隐式图的遍历 NEUOJ544 Problem Description there is an old saying,"You can not do anything without water"or"Water is the source of life". Besides, human beings are made of water. Sister Wang thinks that woman is made of

八数码问题+路径寻找问题+bfs(隐式图的判重操作)

Δ路径寻找问题可以归结为隐式图的遍历,它的任务是找到一条凑够初始状态到终止问题的最优路径, 而不是像回溯法那样找到一个符合某些要求的解. 八数码问题就是路径查找问题背景下的经典训练题目. 程序框架 process()  初始化vis数组,初始化初始节点到目标节点的移动距离 dfs()搜索到每一个节点,如果不是目标节点,对其依次扩展所有子节点,并判重,全部子节点搜索完全后,改变父节点:如果是目标节点成功返回 输出最少移动步数 input: 2 6 4 1 3 7 0 5 8 8 1 5 7 3 6

hdu1818 It&#39;s not a Bug, It&#39;s a Feature!(隐式图最短路径Dijkstra)

题目链接:点击打开链接 题目描述:补丁在修bug时,有时也会引入新的bug,假设有n(n<=20)个潜在的bug和m(m<=100)个补丁,每个补丁用两个长度为n的字符串表示,其中字符串的每个位置表示一个bug.第一个串表示打补丁之前的状态('-'表示在该位置不存在bug,'+'表示该位置必须存在bug,0表示无所谓),第二个串表示打补丁之后的状态('-'表示不存在,'+'表示存在,0表示不变).每个补丁都有一个执行时间,你的任务是用最少的时间把一个所有bug都存在的软件通过打补丁的方式变得没

It&amp;#39;s not a Bug, It&amp;#39;s a Feature! (poj 1482 最短路SPFA+隐式图+位运算)

Language: Default It's not a Bug, It's a Feature! Time Limit: 5000MS   Memory Limit: 30000K Total Submissions: 1353   Accepted: 516 Description It is a curious fact that consumers buying a new software product generally do not expect the software to

uva658(最短路径+隐式图+状态压缩)

题目连接(vj):https://vjudge.net/problem/UVA-658 题意:补丁在修正 bug 时,有时也会引入新的 bug.假定有 n(n≤20)个潜在 bug 和 m(m≤100) 个补丁,每个补丁用两个长度为 n 的字符串表示,其中字符串的每个位置表示一个 bug.第一 个串表示打补丁之前的状态("-" 表示该 bug 必须不存在,"+" 表示必须存在,0 表示无所 谓),第二个串表示打补丁之后的状态("-" 表示不存在,

poj1482(隐式图求最短路)

题目链接 题意:补丁在修正bug时,有时也会引入新的bug.假定有n个潜在的bug m个补丁,每个补丁用两个长度为n的字符串表示,其中字符串的每个位置表示一个bug,第一个串表示打补丁之前的状态('-'表示该bug必须不存在,'+'表示必须存在,0表示无所谓,第二个串表示打补丁之后的状态(-'表示不存在,'+'表示存在,0表示不变).每个补丁都有一个执行时间,你的任务使用最少的时间把一个bug都存在的软件通过打补丁的方式变得没有bug.一个补丁可以打多次. 解法:状压表示每个补丁的存在与否.隐式

hdoj 1226 超级密码 【隐式图BFS】

题目:hdoj 1226 超级密码 分析:这题属于隐式图搜索,状态不是很明显,需要自己建立. 其实搜索说白了就是暴力. 这个题目就是,首先对给出的可以组成的所有的数依次枚举,长度从小到大. 比如第一组样例,因为0不能出现在首位,那么我们枚举首位为1 和 7 看看漫步满足, 满足的话枚举第二位10 11 17 以及 70 71 77 顺便保存他们取余 n 之后的值,这样就可以剪枝,搜索过的就不用重复搜索了. 要求最早出现的BFS即可,第一个搜到的就是. 注意长度不大于500 AC代码: #incl