为防止广告,目前nocow只有登录用户能够创建新页面。如要创建页面请先登录/注册(新用户需要等待1个小时才能正常使用该功能)。

USACO/milk3

来自NOCOW
跳转到: 导航, 搜索

[编辑] 分析

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]是否记录过,这样一来效率大大提高了。


[编辑] 参考代码

c
c++
pascal
Java

个人工具