蓝桥杯--剪邮票(组合问题)java实现

剪邮票
如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。
请填写表示方案数目的整数。

注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

  




解题思路一:

枚举<a, b, c, d, e>五元组合,保证a<b<c<d<e。判断abcde是否连在一起。

判断连在一起的方法:

集合S = {第1张}, 集合T = {其余4张}
for i = 0 … 3:
  for j in T: 

      if 第j张与S中某张相连 then S = S + {第j张}, T = T – {第j张}

例:选择的是3,5,6,7,10 必须保证顺序,再判断是否相连

注意点:1.由于是组合问题,选出的五个数没有顺序之分,在进行组合时由于是递增的选择,所以不需要uesd来判断是否访问过

也不需要判重。

2.在进行节点是否相邻的判断时,需要使用set集合边判断边插入,在java中,不能使用HashSet,要用ConcurrentSkipListSet

java代码如下:

package 剪邮票;


import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;

public class Main {
	/**
	 * 思路1:枚举<a, b, c, d, e>五元组合,保证a<b<c<d<e。判断abcde是否连在一起。
		集合S = {第1张}, 集合T = {其余4张}
		for i = 0 … 3:
		  for j in T: 
		    if 第j张与S中某张相连 then S = S + {第j张}, T = T – {第j张}
	 * @param args
	 */
	static int a[]=new int[5];
	static int ants=0;
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		dfs(0,0);
		System.out.println(ants);
	}
	/**
	 * adj方法:判断x和y数字是否相邻
	 * @param x
	 * @param y
	 * @return
	 */
	public static boolean adj(int x,int y){
		if(x>y){
			int t=x;
			x=y;
			y=t;
		}
		if(y-x==4||(y-x==1&&x%4!=0)) return true;
		return false;
	}
	/**
	 * 判断枚举的五个数是否都满足连在一起
	 * @return
	 */
	public static boolean check(){
		Set<Integer>set=new ConcurrentSkipListSet<Integer>();
		//使用ConcurrentSkipListSet,是因为在foreach遍历时,set集合会被修改,不能使用HashSet
		set.add(a[0]);
		for(int i=0;i<4;i++){
			for(int k=1;k<5;k++){
				for(int x:set){
					if(adj(a[k],x)) set.add(a[k]);
				}
			}
		}
		return set.size()==5;
	}
	public static void dfs(int x,int pre){
		if(x==5){
			if(check()) ants++;
			return ;
		}
		for(int i=pre+1;i<=12;i++){
			a[x]=i;
			dfs(x+1,i);
		}
	}

}

结果为116

方法二:

枚举第1张,第2张……第5张邮票,并且保证第i张与之前的i-1张中至少一张相连,最后需要判重。

例:选择第一张为3,则第二张就要从2,7,4中选。选择3,7,6,5,10 和3,7,6,10,5是一样的,所以需要判重。

判重应用的技巧:使用超级集合Set的嵌套,将枚举好的一个组合a[0-5]转换成set集合,如果重复则加入不到超级集合中,最后超级集合的大小即为所求值。

代码如下:

package 剪邮票2;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class Main {
	static int a[]=new int[5];
	static int ants=0;
	static Set<HashSet<Integer>>set=new HashSet<HashSet<Integer>>();
	static boolean used[]=new boolean[13];
	/**
	 * 超级集合Set用于判重
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Arrays.fill(used, false);
		dfs(0);
		System.out.println(set.size());
	}
	//判断x,y是否相邻
	public static boolean adj(int x,int y){
		if(x>y){
			int t=x;
			x=y;
			y=t;
		}
		if(y-x==4||(y-x==1&&x%4!=0)) return true;
		return false;
	}
	//判断a[0]-a[x-1]中是否有元素与i相邻
	public static boolean check(int x,int i){
		if(x==0) return true;
		for(int k=0;k<x;k++){
			if(adj(a[k],i)) return true;
		}
		return false;
	}
	public static void dfs(int x){
		if(x==5){
			HashSet<Integer>s=new HashSet<Integer>();
			for(int k=0;k<5;k++){
				s.add(a[k]);
			}
			//将当前数组装换成集合加入到超级集合中,最后超级集合的大小就是不重复的满足条件的结果个数
			set.add(s);
			return ;
		}
		for(int i=1;i<=12;i++){
			if(!used[i]&&check(x,i)){//数字i没被用过并且i还有a[0]-a[x-1]中某个数相邻
				a[x]=i;
				used[i]=true;
				dfs(x+1);
				used[i]=false;
			}
		}
	}

}

两方法的区别:方法一是按从小到大的顺序枚举邮票,判断是否相连

方法二是从相连的邮票中选择,判断是否重复


  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DESCRIPTION: 1.Analyze Problem A : sorted stamps array A={ai} ai: one stamp element in array n: array size, that is number of elements in array r: desired value of stamps F(n,r):expected result, minimum number of stamps for a given value r from n size array. S: selected stamps array S={si} 2.Choose Algorithm a.Greedy algorithm seems to be a good choice, try to solve it in O(n), i try divide array into subarry B={bi}, r should larger than every elemnt in B that is r>bi and suppose bk is the smallest element in B, so that r= bk%r, f(i,r)=(bk/r), F(n,r)=∑f(i,r). The main idea is to choose the last element who larger than desired value each time. However,it can not give us optimal solution in some condition, like A={8,5,4,1}, if r=10, this algoritm will give a solution F(n,r)=3, S={8,1,1},but the optimal solution should be F(n,r)=2, S={5,5}. b.Full search so the straight forwards algorithm is to search for every solution in A for desired value directly.However, it will always take O(n!) to go through every combination. c.Dynamic programming, at last, I decide to choose dynamic programming. analyze optimal structure, suppose in A={ai}, for a specific stamp ak,there will be two cases it is choosen so that f(i,r)=1+f(i,r-ak) , 1<=i<=k, r>=ak it is not valid so that f(i,r)=f(i-1,r) 3.Design Dynamic programming optimal structure: Compute-opt(r)= 1 + Compute-opt(r-ai) value: Compute-opt(r) = ∞ (r < 0) Compute-opt(r) = 0 (r = 0) Compute-opt(r) = 1+{Compute-opt(r-ai)} ( 1=<i<=n, r>ai>0 ) Complexity :O(nr) Memory cost:O(n+r) Compute in a bottom-up style to recursive every desired value and array. store value of Compute-opt in memory for future use, so that we can easily get value from those memory in future recursive call, and avoid compute again, that is if the array is not change, you can easily fetch result any desired value j (j < r, r is the value using for compute ). 2.For User totally, I design a small command line for this machine list below 1.Manual Operation 2.Self Auto Testing 3.Check Results q.Quit Manual Operation: when select this machine will turn to be manual mode, ask person to input stamps and desired value; Self Auto Testing:when select this machine will turn to be auto mode, show the test case already design in code after that machine will quit automatically. Check Results: only be visiable in Manual Operation, people can check desired value for the array input before, the desired value should be no more than first time input. Quit, clean all the memory and quit system.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值