求子集问题

从订阅博客中看到这道题,于是花了上午的时间写了一下,此题类似于0-1背包问题,故复习了一下,分别有递归和非递归的解法。

题目:给定一个数t,以及n个整数,在这n个整数中找到相加之和为t的所有组合,例如t = 4n = 6,这6个数为[4, 3, 2, 2, 1, 1],这样输出就有4个不同的组合,它们的相加之和为44, 3+1, 2+2, and 2+1+1。请设计一个高效算法实现这个需求。

递归解法:

    private static int[] a={4,3,2,2,2,1,1};

    private static int t=8;

    private static int n=7;

    private static boolean[] flags;

   

    public static int subNum(int s,int r){

        if(s==0){

            output(0);

            // System.out.println(r);

            return 1;

        }else{

            if(s<0 || (s>0 && r>=n)){

                return 0;

            }else{

                flags[r]=true;

                int f=subNum(s-a[r],r+1);

// while (r+1<n && a[r+1] == a[r])

// r++;

                flags[r]=false;

                f=subNum(s,r+1);

                return 0;

                /*

                 * if(subNum(s-a[r],r+1)==1){ //System.out.println(s+" "+r);

                 * return 1; }else{ flags[r]=false; return subNum(s,r+1); }

                 */

            }

        }

    }

 

非递归算法:

private final static int N = 20;

 private static int W[] = { 1, 1, 1, 2, 2, 3, 4, 4, 5, 5, 5, 8, 8, 8, 10,
   10, 10, 12, 12, 12 };

 // private static int W[]={4,3,2,2,2,1,1}; // 若原W无序,则先对其排序
 private final static int T = 20;

 private static int stack[] = new int[20];

 private static int stackIdx;

 private static void output() {
  int i;
  for (i = 0; i < stackIdx; i++) {
   System.out.print(W[stack[i]] + " ");
  }
  System.out.println();
 }

 private static void outstack() {
  int i;
  for (i = 0; i < stackIdx; i++) {
   System.out.print(stack[i] + " ");
  }
  System.out.println();
 }

 public static void subNum() {
  int idx;
  int sum;
  idx = 0;
  sum = T;
  stackIdx = 0;
  while (stackIdx >= 0) {
   // System.out.println(" "+stackIdx+ " "+sum+" "+idx);
   if (idx >= N) {
    --stackIdx;
    if (stackIdx < 0)
     break;
    sum += W[stack[stackIdx]];
    idx = stack[stackIdx];
    while (idx + 1 < N && W[idx + 1] == W[idx])
     idx++;//
前后的元素相同,即可以跳过后面相同的元素,因为已经在前面一个元素遍历过

    ++idx;
   } else {
    sum -= W[idx];
    stack[stackIdx++] = idx;
    // System.out.println(" "+stackIdx+ " "+sum+" "+idx);
    if (sum == 0) {
     output();
     --stackIdx;
     sum += W[idx];
     while (idx + 1 < N && W[idx + 1] == W[idx])
      idx++;//
找到符合的条件,而下一次遍历的元素跟符合条件中的最后一个元素相同,即表明有重复相同的组合
     ++idx;
    } else if (sum > 0) {
     ++idx;
    } else {
     --stackIdx;
     if (stackIdx < 0)
      break;
     sum += W[idx];
     idx = stack[stackIdx] + 1;
     // while(idx+1<N && W[idx+1] == W[idx]&& flag)
     // idx++;
    }
   }
  }

 }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值