贪心算法-泊松分酒问题
来源:互联网 发布:音频剪辑 for mac 编辑:程序博客网 时间:2024/06/05 17:06
讲这道题纯粹就是比较好玩,就记录一下.泊松分酒是很著名的一道题,讲的是假设某人有12品脱的啤酒一瓶,想从中倒出六品脱,但是恰巧身边没有6品脱的容器,仅有一个8品脱和一个5品脱的容器,怎样倒才能将啤酒分为两个6品脱呢?
代码:
import java.util.LinkedList;import java.util.Set;public class Oil { static class Status{ static int[] full={12,8,5};//满的状态 int[] bottle=new int[3];//瓶子的状态 Status from;//从哪个状态来的 public Status(int a,int b,int c){ bottle[0]=a; bottle[1]=b; bottle[2]=c; } //获取某种状态开始下一步的所有的状态 public Set opreation(){ Set res=new HashSet(); //开始倒酒 for(int i=0;i<bottle.length;i++){ for(int j=0;j<bottle.length;j++){ if(i==j) continue; //不倒自己 if(bottle[i]==0) continue;//自己是空的 不倒 if(bottle[j]==full[j]) continue;//对方是满的 不倒 Status t=new Status(bottle[0], bottle[1], bottle[2]); t.from=this;//从自己这个状态开始变化 //真的开始倒酒了 t.bottle[j]+=t.bottle[i]; t.bottle[i]=0; if(t.bottle[j]>full[j]){//装不下了 t.bottle[i]=t.bottle[j]-full[j];//满的倒回去 t.bottle[j]=full[j]; } res.add(t); } } return res; } //是否含有某种状态 public boolean has2(int x){ int index=0; if (bottle[0]==x) index++; if (bottle[1]==x) index++; if (bottle[2]==x) index++; return index==2?true:false; } public Status getFrom() { return from; } public String toString(){ return "<" + bottle[0] + "," + bottle[1] + "," + bottle[2] + ">"; } public int hashCode() { return 100; } public boolean equals(Object obj) { Status x=(Status)obj; return bottle[0]==x.bottle[0]&&bottle[1]==x.bottle[1]&&bottle[2]==x.bottle[2]; } } public static void main(String[] args) { Set<Status> all=new HashSet<Status>();//存放所有结果状态 all.add(new Status(12, 0, 0)); for(;;){ Set newset=new HashSet(); for(Status x:all){//所有上一种状态产生所有下一种状态 Set t = x.opreation(); newset.addAll(t); } if(all.containsAll(newset)) break;//出口 all.addAll(newset); } LinkedList<Status> list=new LinkedList<Status>();//存放有6的一溜 for(Status k:all){ if(k.has2(6)){ while(k!=null){ list.push(k); k=k.getFrom();//从终止状态开始往上追溯 } } } //输出 while(!list.isEmpty()){ System.out.println(list.pop()); } } }
这个解法找到的其实是最优解,至于为什么呢,其实利用set的方法十分巧妙,结果集set里随着一次次的分酒一次次地扩增,当第一次出现含有两个6的状态的时候,再往前追溯,步骤是最少的!因为这个我们想要的状态是第一次出现.
假如我们每次都打印出all集合,可以知道,当第一次找到含有两个6状态的时候程序并没有结束,因为还没有找到所有的状态.
而后面的状态再进行分酒时,仍有可能产生两个6的状态,但是想要加入set集合的时候就行不通了,所以此程序只输出最早加入的那一个解,并且是最优的.
当然这种算法并不能输出所有的解,如果要得到所有的解,我们可以采用以下算法,这种算法借鉴了图的深度搜索(DFS)以及回溯的技巧,需要注意的是,和8皇后问题一样,需要回溯的时机有两个,出错的时候和找到某一组解的时候.
代码:
import java.util.ArrayList;import java.util.List;import java.util.Scanner;public class Oil { int[] full = new int[3]; //满状态 容量 int[] bottle = new int[3]; //瓶子的状态 int target = 0; //目标 List<int[]> res = new ArrayList<int[]>();//存放结果 public void opreation(int[] bottle) { for(int i=0;i<3;i++) { for(int j=1;j<3;j++){//每个瓶子都不往自己倒 总共6种可能性 int[] temp = bottle.clone();//每次循环都创建临时数组 int to=(i+j)%3;//(i+j)%3 是除每种i瓶子外其他两个瓶子的序号,即要倒的目标 if(temp[i]==0) continue;//自己是空的 不倒 if(temp[to]==full[to]) continue;//对方是满的 不倒 //开始倒酒 temp[to]+=temp[i]; temp[i]=0; if(temp[to]>full[to]){//装不下了 temp[i]=temp[to]-full[to];//满出来的部分倒回去 temp[to]=full[to]; } if(had(temp)) continue;//检测是否已经存在相同状态,防止重复 res.add(temp);//添加到结果链表 if(has2(temp)) return;//如果找到有两个想要的状态的结果就返回 opreation(temp);//继续下一次分酒 res.remove(res.size()-1); //回溯 仔细体会 } } } //是否以及含有状态 private boolean had(int[] bottlex) { for(int[] e:res) if(e[0]==bottlex[0]&&e[1]==bottlex[1]&&e[2]==bottlex[2]) return true; return false; } //检测找到结果 private boolean has2(int[] bottle) { int index=0; for(int i=0;i<bottle.length;i++) if(bottle[i]==target) index++; if(index==2){ show(res);//输出 res.remove(res.size()-1);//回溯 return true; } return false; } //打印 private void show(List<int[]> res) { for(int[] e:res) { System.out.println(e[0] + "," + e[1] + "," + e[2]); } System.out.println(); } public static void main(String[] args) { Oil o = new Oil(); Scanner scanner = new Scanner(System.in); String s =""; if(scanner.hasNext()) { s = scanner.nextLine(); } String[] data = s.split(","); int[] d = new int[data.length]; for(int i=0;i<data.length;i++){ d[i] = Integer.parseInt(data[i]); } o.full = new int[]{d[0],d[1],d[2]}; o.bottle = new int[]{d[3],d[4],d[5]}; o.target = d[6]; o.res.add(new int[]{d[3],d[4],d[5]});//添加初始状态 o.opreation(o.bottle); }}
显然,按照深度搜索并不能有效地找到最优解.上面两种算法都是比较巧的,我也比较喜欢.
如果要同时找到所有解和最优解,用图的广度搜索(BFS)会很方便,这也是网上采用的最多的,代码到处都有,就不写了.
1 0
- 贪心算法-泊松分酒问题
- 贪心算法 - 背包问题
- 贪心算法,重载问题
- 贪心算法相关问题
- 贪心算法:硬币问题
- 贪心算法证明问题
- 贪心算法----背包问题
- 【贪心算法】:背包问题
- 贪心算法-背包问题
- 贪心算法 背包问题
- 贪心算法-背包问题
- 【贪心算法】背包问题
- 贪心算法-背包问题
- 贪心算法--过河问题
- 贪心算法--背包问题
- 贪心算法--装箱问题
- 贪心算法----装箱问题
- 贪心算法--装箱问题
- 关于ZIGBEE端点的一点理解
- Socket深入学习
- RTLinux 和 Xenomai 系统在mini2440的移植——usb无线网卡死机问题
- 修改Ubuntu16.04文件数、进程数限制!
- CentOS6.7 下搭建 Hadoop2.7.1 单机伪分布式环境
- 贪心算法-泊松分酒问题
- iOS对象内存结构中的 isa 指针是用来做什么的?
- js中for语句
- 发现css超过字数就用省略号代替的一个属性
- JAVA小白的奋斗之旅:面向对象
- 2. 操作系统引导
- Android 多媒体之视频
- WebApi 接口参数传参详解
- 看图说maven系列(二)