子集和问题

来源:互联网 发布:python help 函数 编辑:程序博客网 时间:2024/06/01 14:02

子集和问题:

集合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[n][j]=0;for(j=w[n];j<=c;j++)      /*首先计算m(n,j)*/   m[n][j]=w[n];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[n];

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[N],m[N][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[n];j++)      m[n][j]=0;          /*确定初始条件*/   for(j=w[n];j<=c;j++)      m[n][j]=w[n];   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[n])   {      printf("%d",w[n]);      sb+=w[n];   }   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中的元素不能太多,也不能太大,从而限制了该程序的应用范围

1 0
原创粉丝点击