为防止广告,目前nocow只有登录用户能够创建新页面。如要创建页面请先登录/注册(新用户需要等待1个小时才能正常使用该功能)。
USACO/milk3
[编辑] 分析
Way 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]也行 也就是说三维的也行
Way 2 这个题目首先让我想到的就是递归…不过我不知道临界点是多少,就乱试了,结果用i=14能过…不过我不知道为什么i要等于14,没证明过当i等于14时能包括大多数的过程… (骆驼绒同学补充了一下,他说用BFS。只有在扩充的状态没有出现过的情况下,才把它加入队列,这样可以保证不重不漏)
Way 3 人脑解法 这题简直是赤裸裸的奥数啊,直接手动算出所有的解再让计算机输出了就行了!估计只用0微秒 由于题目不对称(a要为0),导致了源代码的丑陋。分a小于b和a大于等于b两种情况,每种情况各有两种不同的方法,不断用b灌a,或者反之,如果灌完了就用c充满继续,如果充不满就说明做完了,如果灌满了还有就把被灌的倒回c,然后继续灌。最多做20次循环,因为c<=20 最后输出很简单,先遍历数组到c-1,之后再writeln(c)就行(c永远是一个解)
这里用到了一个输出技巧,当你不知道最后一个输出数据是什么而且又不能留空格时,我先把所有数据加空格存到一个String里,然后再把String里的最后一位去掉就OK了…就像这样
for i:=0 to 20 do if bo[i] then begin str(i,strr); tmpstr:=tmpstr+strr+' '; end; delete(tmpstr,length(tmpstr),1);
这个输出技巧显然也可以这样使用
first:=true; for i:=0 to 20 do if s[i] then if first then begin first:=false; write(i); end else write(' ',i); writeln;
在procedure里的思想是这样的
for 倒奶的桶→a to c for 灌奶的桶→a to c if 倒奶的桶=灌奶的桶 or 倒奶的桶的牛奶=0 or 灌奶的桶满了 then continue else 开始倒奶(有3种可能,其实可以合并成2种,为了小心起见我还是分成3种) 到下一个此过程
骆驼绒同学认为,比如,若将a数组中的偶数按原顺序输出,且保证"X X X X"的格式,那么可以这样:
for i:=1 to na do if a[i] mod 2=0 then begin write(a[i]);break;end; for j:=i+1 to na do if a[j] mod 2=0 then write(' ',a[j]);
于是,节省时间了。
骆驼绒同学他又有话说,他说比如将a桶的牛奶倒到b桶,那么一个巧妙的方法是:
procedure qab; begin tmp:=a+b;tb:=min(tmp,bb);ta:=tmp-tb;tc:=c; end;
这里a,b,c是到之前的状态,ta,tb,tc是倒之后的状态,aa,bb,cc是容量。
可以对照这些分析看一下他的程序。
way4:
使用枚举的方法,按照顺序A,B,C寻找每一个牛奶非空的桶,假设我们找到了B, 则接下来只能是从B-》A或B-》C或则什么都不做这三种情况;使用递归来完成。 我们需要一个数组state[500][3]来储存遍历过的三个桶中牛奶的状态, 当 当前状态已经在之前出现过,就退出。同样也是使用一个数组bool leave_c[]来储存C中可能的牛奶量。 具体代码可以参考(c++ ID=herohan1).这样或许更容易理解。
way5:
使用二进制保存状态,因为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, 用一个数组保存已经判断过的状态 具体代码可以参考(C++ ID=hankjin1)
way6:
思维模式还是BFS, 把每种状态压成了一个点,如果能从状态k1转换到k2,[k1,k2]:=true; 即相当于连一条有向边。 然后不断更新,直到不能产生新的状态,最后扫描一次A桶为0的状态的点是否能到达即可。参看pascal某(name:恶心的BFS)代码。
way7:模拟n次
设一个标记用的数组 f[0..20,0..20,0..20]记录f[a,b,c]的情况是否出现过... 6种倒法用递归循环一下..边界出口就是当前a,b,c内的牛奶量曾出现过
也就是 f[a,b,c]=true 时。。。程序巨短》。时间和其他差不多。。性价比 较高Ancy 参见程序.....有标注way:6
way 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]是否记录过,这样一来效率大大提高了。