您的位置:首页 > 编程语言 > C语言/C++

子集和问题

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)

我们期望的最大值是两者中的最大值,于是有递推关系:

| 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中的元素不能太多,也不能太大,从而限制了该程序的应用范围
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息