子集和问题
2017-01-03 16:11
190 查看
子集和问题:
集合s是一个有n个正整数构成的集合{w1,w2,……,wn},指定正整数c,判断正整数c,判断是否存在S的一个子集S1,使得w∈S1,∑w=c;
若子集和问题有解,则输出子集S1的元素;
1.说明:
设n个元素(正整数)存储在w数组中,应用动态规划设计求解;
目标函数: max(n)∑(i=1)xi wi
约束条件: (n)∑(i=1)xi wi<=c(xi∈{0,1},c,wi∈N*,i=1,2,……,n)
按构建S1选择每一个元素为一个阶段,共分为n个阶段;
(1)、建立递推关系;
设m(i,j)为集合S1距离c还差j,可取整数编号范围为:i,i+1,……,n的最大和,则:
当0<=j< w(i)时,整数w(i)不可选,m(i,j)与m(i+1,j)相同;
当j>=w(i)时,有两种选择:不选择整数w(i),这时最大值为m(i+1,j);选择整数w(i),这时已增加整数w(i),剩余差额为j-w(i),可以选择整数编号范围为i+1,……,n,最大值为m(i+1,j-w(i))+w(i);
我们期望的最大值是两者中的最大值,于是有递推关系:
以上j与w(i)均为正整数,i=1,2,……,n,所求最优值为m(1,c);
(2)、递推计算最优值;
(3)、构造最优解;
构造最优解即选择构建集合S1的所有元素;
2.程序设计:
3.程序运行示例及其注意事项:
注意:程序设置了二维数组,限制了集合S中的元素不能太多,也不能太大,从而限制了该程序的应用范围;
集合s是一个有n个正整数构成的集合{w1,w2,……,wn},指定正整数c,判断正整数c,判断是否存在S的一个子集S1,使得w∈S1,∑w=c;
若子集和问题有解,则输出子集S1的元素;
1.说明:
设n个元素(正整数)存储在w数组中,应用动态规划设计求解;
目标函数: max(n)∑(i=1)xi wi
约束条件: (n)∑(i=1)xi wi<=c(xi∈{0,1},c,wi∈N*,i=1,2,……,n)
按构建S1选择每一个元素为一个阶段,共分为n个阶段;
(1)、建立递推关系;
设m(i,j)为集合S1距离c还差j,可取整数编号范围为:i,i+1,……,n的最大和,则:
当0<=j< w(i)时,整数w(i)不可选,m(i,j)与m(i+1,j)相同;
当j>=w(i)时,有两种选择:不选择整数w(i),这时最大值为m(i+1,j);选择整数w(i),这时已增加整数w(i),剩余差额为j-w(i),可以选择整数编号范围为i+1,……,n,最大值为m(i+1,j-w(i))+w(i);
我们期望的最大值是两者中的最大值,于是有递推关系:
| m(i+1,j) 0<=j<w(i) m(i,j)= | max(m(i+1,j),m(i+1,j-w(i))+w(i)) j>=w(i)
以上j与w(i)均为正整数,i=1,2,……,n,所求最优值为m(1,c);
(2)、递推计算最优值;
for(j=0;j<=c;j++) m [j]=0; for(j=w ;j<=c;j++) /*首先计算m(n,j)*/ m [j]=w ; for(i=n-1;i>=1;i--) /*递推计算m(i,j)*/ for(j=0;j<=c;j++) if(j>=w[i] && m[i+1][j]<m[i+1][j-w[i]]+w[i]) m[i][j]=m[i+1][j-w[i]]+w[i]; else m[i][j]=m[i+1][j]; printf("%d",m[1][c]);
(3)、构造最优解;
构造最优解即选择构建集合S1的所有元素;
if(m[i][cw]>m[i+1][cw]) /*其中cw为当前与c差额,i=1,2,n-1*/ 选择w[i]; else 不选择w[i]; if(所选整数和!=m(1,c)) 选择w ;
2.程序设计:
#define N 100 #include<stdio.h> #include<stdlib.h> #include<time.h> int main() { int n,c,i,j,s,t,cb,sb,w ,m [10*N]; t=time(0)%1000; srand(t); /*随机数发生器初始化*/ printf("请确定S中正整数的个数n: "); scanf("%d",&n); printf("已知集合S的%d个正整数为: \n",n); for(s=0,i=1;i<=n;i++) /*随机产生n个正整数*/ { w[i]=rand()%(5*n)+10; s+=w[i]; printf("%3d",w[i]); } printf("\n请指定和c: "); scanf("%d",&c); if(c>s) { printf("集合所有整数之和不足,无解!\n"); return; } if(c==s) { printf("集合所有元素之和为%d!\n",c); return; } for(j=0;j<=w ;j++) m [j]=0; /*确定初始条件*/ for(j=w ;j<=c;j++) m [j]=w ; for(i=n-1;i>=1;i--) /*递推计算m(i,j)*/ for(j=0;j<=c;j++) if(j>=w[i] && m[i+1][j]<m[i+1][j-w[i]]+w[i]) m[i][j]=m[i+1][j-w[i]]+w[i]; else m[i][j]=m[i+1][j]; /*得最优值m(1,c)*/ if(m[1][c]!=c) { printf("无解!\n"); return; } else printf("所选元素为:"); cb=m[1][c]; for(sb=0,i=1;i<=n-1;i++) /*构造并输出所选元素*/ if(m[i][cb]>m[i+1][cb]) { cb-=w[i]; sb+=w[i]; printf("%3d",w[i]); } if(m[1][c]-sb==w ) { printf("%d",w ); sb+=w ; } printf(" (%d)\n",sb); }
3.程序运行示例及其注意事项:
请确定S中正整数的个数n: 15 已知集合S的15个正整数为: 44 19 19 41 10 35 38 43 43 22 40 69 11 76 29 请指定和c: 300 子集和问题有解! 所选元素为: 43 43 40 69 76 29 (300)
注意:程序设置了二维数组,限制了集合S中的元素不能太多,也不能太大,从而限制了该程序的应用范围;
相关文章推荐
- p倍和子集问题
- MSDN两天没有办法使用, 提示"当前子集中没有该选项,请选择其他子集",问题解决了
- 集合子集问题:
- 【NPC】13、三维匹配规约到子集和问题
- 子集和问题
- 【NPC】14、子集和问题规约到划分问题
- 判断回文字符串与子集和问题
- 求集合所有子集问题
- 用回溯法解决子集和问题【C#版本】
- 简单背包问题(实际上是子集问题)
- 子集与子集和问题(Subset sum)的递归回溯解
- 子集和问题
- 两个序列最大子集公共和问题
- [动态规划]背包问题(找零/子集和/编辑距离)
- 求集合子集问题
- 子集和问题 —— 一种组合生成算法
- 集合的子集和集合的全排列问题
- 子集和的另外一个问题
- 8603 子集和问题
- hdu 2062 子集问题