题解 P1215 【[USACO1.4]母亲的牛奶 Mother's Milk】
发现没有c++的题解,果断来一发。
想出了好几种解法,我就一点一点地写吧。
方法 1:DFS 因为牛奶的总量是不变的,所以可以用a,b中的牛奶量做状态,初始状态是(0,0),每次只能有6种选择,a倒b,a倒c,b倒a,b倒c,c倒a,c倒b。用一个数组vis[i][j]判重,s[i]记录c中所有可能值(s[i]=true表示c中可能出现i),如果当前状态是(0,x),那么s[mc -x]=true,最后输出s中所有true的就可以了。 用vis[i][j][k]也行,也就是说三维的也行。
方法 2 这个题目也可以递归…不过我不知道临界点是多少,就乱试了,结果用i=14能过…不过我不知道为什么i要等于14,没证明过当i等于14时能包括大多数的过程… (假如用BFS。只有在扩充的状态没有出现过的情况下,才把它加入队列,这样可以保证不重不漏)
方法3 人脑解法 这题简直是赤裸裸的奥数啊,直接手动算出所有的解再让计算机输出了就行了!估计只用0微秒 由于题目不对称(a要为0),导致了源代码的丑陋。分a小于b和a大于等于b两种情况,每种情况各有两种不同的方法,不断用b灌a,或者反之,如果灌完了就用c充满继续,如果充不满就说明做完了,如果灌满了还有就把被灌的倒回c,然后继续灌。最多做20次循环,因为c<=20 最后输出很简单,先遍历数组到c-1,之后再输出(c)就行(c永远是一个解)
方法4 使用枚举的方法,按照顺序A,B,C寻找每一个牛奶非空的桶,假设我们找到了B, 则接下来只能是从B-》A或B-》C或则什么都不做这三种情况;使用递归来完成。 我们需要一个数组state[500][3]来储存遍历过的三个桶中牛奶的状态, 当 当前状态已经在之前出现过,就退出。同样也是使用一个数组bool leave_c[]来储存C中可能的牛奶量。.这样或许更容易理解。
方法5 使用二进制保存状态,因为A,B,C都在1-20之间,所以可以分别用8位表示他们的容量
int a = (status&0xFF0000)>>16;
int b = (status&0x00FF00)>>8;
int c = (status&0x0000FF);
然后分别对a->b, a->c, b->c, b->a, c->a, c->b六种情况进行DFS, 用一个数组保存已经判断过的状态。
方法6 思维模式还是BFS, 把每种状态压成了一个点,如果能从状态k1转换到k2,[k1,k2]:=true; 即相当于连一条有向边。 然后不断更新,直到不能产生新的状态,最后扫描一次A桶为0的状态的点是否能到达即可。
方法7 模拟n次
设一个标记用的数组 f[0..20,0..20,0..20]记录f[a,b,c]的情况是否出现过...
6种倒法用递归循环一下..边界出口就是当前a,b,c内的牛奶量曾出现过
也就是 f[a,b,c]=true 时。。。程序巨短》。时间和其他差不多。。性价比 较高
方法8 将i从0到c带入一个bool函数f(i定义:c桶为i,a桶空),f的功能是bfs遍历到状态为(x,x,i),判断能否遍历到或者此时的状态a是否为0;这样做效率貌似是很低的,不过可以加一个数组p[],设定为全局变量,在遍历时遇到(0,x,i)的状态就记录,另外在将i带入f前判断p[i]是否记录过,这样一来效率大大提高了。
上代码
(减少代码复制,创造美好洛谷)
#include <cstdio>
#include <cstring>
#include <algorithm>
const int MAX = 22;
bool milk[MAX] = {0};
bool vis[MAX][MAX][MAX] = {0};
static int bkt[3];
void dfs(int a[]) {
if (vis[a[0]][a[1]][a[2]]) return;
vis[a[0]][a[1]][a[2]] = true;
if (a[0] == 0) milk[a[2]] = true;
//six ways to check, from bkt i to bkt j
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (j == i) continue;
if (a[j] < bkt[j] && a[i] > 0) {
int rec = std::min(bkt[j] - a[j], a[i]);
int b[3];
memcpy(b, a, sizeof(int)*3);
b[i] -= rec, b[j] += rec;
dfs(b);
}
}
}
int main() {
//freopen("milk3.in", "r", stdin);
//freopen("milk3.out", "w", stdout);
int &A = bkt[0], &B = bkt[1], &C = bkt[2];
scanf(" %d %d %d", &A, &B, &C);
int a[3] = {0,0,C};
dfs(a);
for (int i = 0; i < C; ++i) {
if (milk[i]) printf("%d ", i);
}
printf("%d\n", C);
return 0;
}