寻找和为定值的任意个数(01背包)

这个问题是在July的《编程之法》上看到的,题目要求是:输入两个数字n与sum,在1~n中寻找哪些数字之和等于sum。

很明显这是个典型的01背包问题,因为每一个元素只有要与不要两种选择~

#include<list>  
#include<iostream>  
using namespace std;  
  
list<int>list1;  
void find_factor(int sum, int n)   
{  
    // 递归出口  
    if(n <= 0 || sum <= 0)  
        return;  
    if(n>sum)
        n=sum;
      
    // 输出找到的结果  
    if(sum == n)  
    {  
        // 反转list  
        list1.reverse();  
        for(list<int>::iterator iter = list1.begin(); iter != list1.end(); iter++)  
            cout << *iter << " + ";  
        cout << n << endl;  
        list1.reverse();      
    }  
      
    list1.push_front(n);      //典型的01背包问题  
    find_factor(sum-n, n-1);   //放n,n-1个数填满sum-n  
    list1.pop_front();  
    find_factor(sum, n-1);     //不放n,n-1个数填满sum   
}  
  
int main()  
{  
    int sum, n;  
    cout << "请输入你要等于多少的数值sum:" << endl;  
    cin >> sum;  
    cout << "请输入你要从1.....n数列中取值的n:" << endl;  
    cin >> n;  
    cout << "所有可能的序列,如下:" << endl;  
    find_factor(sum,n);  
    return 0;  
}  

后来自己又在想,这样局限性有点大,如果数组元素是任意的呢?即: 数组元素任意,输入sum,求所有和为sum的组合

可以思考一下:若数组元素任意,则处理之前,必须进行排序(原本可能是无序的),使用快速排序实现此功能。然后实际上还是一个背包问题,只不过不是01背包,可以说算是完全背包吧,因为每个元素科可以有多个,但是在逻辑上仍然将它们看作是单独的存在,两种选择:要或不要,在上边代码的基础上进行改进:

#include<iostream>
#include<cstdio>
#include<list>
using namespace std;

list<int> list1;
int number;  //统计总共多少种符合要求的组合 


int find(int s[],int sum){        //在数组s中寻找比sum小的最大的一个元素的下标,若不存在则返回-1 
	int i;
	for (i=0;s[i]<=sum&&i<10;i++);
	return i-1;
}

void swap(int *a,int *b){       //交换数组中两个元素的下标 
	int t;
	t=*a;
	*a=*b;
	*b=t;
}

void sort(int s[],int left,int right){	  //快速排序 
	if(left>=right)
	   return ;
	
	int i,j,mid;
	mid =(left+right)/2;
	swap(&s[left],&s[mid]);
	for(j=left,i=left+1;i<=right;i++)
		if(s[i]<s[left]){
			swap(&s[i],&s[++j]);
		}
	swap(&s[left],&s[j]);
	
	sort(s,left,j-1);
	sort(s,j+1,right);
		
}

void find_factor(int s[],int sum,int n){    
	if(n==-1)          //n==-1则相当于:s[0]被考虑过(包含加或不加)后仍旧不能满足sum,即之前的组合状态不可行,直接返回。 
	  return ;
	
	if(sum<s[n]){     //如果sum小于当前区域内的最大元素,则执行find()函数找到小于它的最后一个元素,若找不到,则说明在 
	  n=find(s,sum);   // 之前的组合状态不可行,直接返回。 
	  if(n==-1)
	  return;	
    }
	
	if(sum==s[n]){    //说明之前的组合状态可行 
		number++;      //统计所有符合要求的组合的个数 
		list1.reverse();  //反转list	 
		for(list<int>::iterator iter =list1.begin();iter!=list1.end();iter++)
			cout<<*iter<<"+";
		cout<<s[n]<<endl;	
		
		list1.reverse();  		
	}
	
	list1.push_front(s[n]);    //非常典型的01背包问问题 
	find_factor(s,sum-s[n],n-1);//放s[n],用剩余n-1个元素 填满 sum-s[n]  
	list1.pop_front();
	find_factor(s,sum,n-1);    //不放s[n],用剩余n-1个元素 填满 sum
	
}

 

int main(void){
	
	int i,j,k,sum;
	int s[10];
	printf("请输入想要的sum:\n");
	scanf("%d",&sum);
	printf("请输入数组元素:\n");
	for(i=0;i<10;i++)
		scanf("%d",s+i);

	sort(s,0,9);               //快排 
/*	for(i=0;i<10;i++){
		printf("%d ",s[i]);
	}
*/
 
    printf("符合要求的组合:\n");
	find_factor(s,sum,9);
	printf("总共%d种组合\n",number);
    
    

    
	 
	
	
	
	
	return 0;
}

如此。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值