zoukankan      html  css  js  c++  java
  • 组合算法:产生基本组合对象(Generating Elementary Combinatorial Objcts)

      组合对象,算法课刚学的时候我也挺小看他的。之后在写算法,特别是蛮力法,的时候才发现他的重要性。

      先说说他的用处。在算法问题中常遇到一些组合问题,如:

      给你一个集合S = {2, 7, 36, 40, 53, 59, 62, 69, 77, 80, 87, 89, 95, 98, 100, 102, 103, 106, 112, 115},要你从S中找出他的子集T,使得T中所有元素的和为220。

      当然,这个问题不光可以用蛮力来解决。但如果用蛮力法的话,我们有没有最简便的方法,使得算法效率最高呢。这就要用到组合对象了。

      组合对象X = {xi},我的理解,它是一个与集合S = {si}有相同元素个数的集合,并且X与S中的元素一一对应,X中的每个元素都只有两个值,a和b,且:xi = a 时,si被选中;xi = b 时,si没被选中。(大部分情况下我们默认a = 1,b = 0)

      下图为组合对象的一个例子,其中T是全集S={1,2,3}的一个子集合,他是根据组合对象的取值决定的。

               

      可以看到,如果我们能有个方法能够迭代的依次生成出所有的组合对象,那么对于全集S的不同子集合的选取将会方便很多。而且现在已经有了不少牛人的研究成果,用他们的算法绝对比我们自己做要省事,关键是效率很高。

      以下介绍Lexicographic ordering和Gray codes,具体事例都以S为全集,T为子集,且|S| = |T| = 3。元素索引从左向右递增,首位索引为0。

      Lexicographic ordering

      {0,0,1}
      {0,1,0}
      {0,1,1}
      {1,0,0}
      {1,0,1}
      {1,1,0}
      {1,1,1}
      {0,0,0}

      算法思想:指针每次从右侧开始向左扫描:  //以{0,0,1}为例

      (1)每遇到1,则将其改为0,并继续往左扫描下一位,即指针指向下一位;  //组合对象改为{0,0,0},指针指向红色元素

       (2)若指针索引为-1,即遇到{1,1,1}这种情况,则算法结束;  //若以{1,1,1}为例,此时组合对象为{0,0,0},算法结束

      (3)否则,将指针所指位改为1,并结束扫描;  //接(1)中例子:此时指针所指元素改为1,即{0,1,0},此为下一个组合对象

      此时得到下一种组合对象

      以下为代码,供参考:

    public boolean[] next() {
    	if(!hasNext) return null;    //组合对象生成完毕
    	int index = n - 1;
    	while(index >= 0 && set[index]){
    		set[index] = false;
    		index--;
    	}
    	if(index == -1){
    		hasNext = false;
    	}else{
    		set[index] = true;
    	}
    	return set;
    }
    

      Gray codes

      {0,0,0}
      {0,0,1}
      {0,1,1}
      {0,1,0}
      {1,1,0}
      {1,1,1}
      {1,0,1}
      {1,0,0}

          

      算法思想:如上图所示,组合对象的产生是遵循固定规律的,即沿着图中所绘图形的边框呈“之”字形变化:

      (1)当沿着直线向上或向下时,仅将最右边一位做取反操作,即得下一个组合对象;

      (2)当沿着直线向右时(向左可以类推):

        a.当在(1)中取反操作是0置1时,此时只要将右边第二位置反,即得下一个组合对象;

        b.否则,从右向左扫描找到第一个1的索引 i ,若 i = 0,即 i 为最左边的索引,则不再有下一个组合对象,算法终止,否则将第 i - 1位置反,即得下一个组合对象;

      代码如下,仅供参考:

    //数组索引从0到n
    public boolean[] next(){
    	if(begin){	//如果是第一次调用begin=true,则返回{0,0,0,...,0}
    		begin = false;
    		return set;
    	}
    	if(!hasNext) return null;	//如果没有下一个组合对象 返回空
    	if(turnFlag){	//为向下或向上移动
    		if(!set[n]){
    			set[n] = !set[n];	//0变1
    			index = n - 1;
    		}else{
    			set[n] = !set[n];	//1变0
    			index = n - 1;
    			while(!set[index]) index--;	//如果当前位为0继续向左移
    			if(index == 0){
    				hasNext = false;	//此时为100...的情况 说明已经到最后了 没有next了
    			}else{
    				index--;	//此时为...100...情况 还有next
    			}
    		}
    		turnFlag = false;
    	}else{	//为向右移动
    		set[index] = !set[index];
    		turnFlag = true;
    	}
    	return set;
    }
    

     

     

  • 相关阅读:
    HDU4507 吉哥系列故事――恨7不成妻(数位dp)
    UCF Local Programming Contest 2017 G题(dp)
    ICPC Latin American Regional Contests 2019 I题
    UCF Local Programming Contest 2017 H题(区间dp)
    HDU2089 不要62
    AcWing1084 数字游戏II(数位dp)
    UCF Local Programming Contest 2017 F题(最短路)
    Google Code Jam 2019 Round 1A Pylons(爆搜+贪心)
    AcWing1083 Windy数(数位dp)
    Vue
  • 原文地址:https://www.cnblogs.com/wu8685/p/1924280.html
Copyright © 2011-2022 走看看