这是我参与 8 月更文挑战的第 8 天,活动详情查看: 8月更文挑战
子集和问题
问题描述:
-
𝑆 = {𝑥1, 𝑥2, … , 𝑥𝑛}为正整数集合,c是一个 正整数。
-
子集和问题就是判断是否存在 S 的一个 S1 , 使得S1中所有元素的和为c。
求解思路:
对于S={a1,a2,...,an},每个元素只有取与不取两种情况,再考虑它们的和是否等于M,但是这样的情况共有2^n中,这种算法的效率显然是不行的。
动态规划(备忘录法):
用一个二维数组来保存结果,避免重复过程。
如下图,填完后输出右下角值(opt[n-1][key]])
| P数组 \ key值 | 0 | 1 | 2 | 3 | 4 | 5 | |
|---|---|---|---|---|---|---|---|
| p[0]:10 | 1 | 0 | 0 | 0 | 0 | 0 | |
| P[1]: 5 | 1 | 0. | 0 | 0 | 0 | 1 | |
| P[2]: 2 | 1 | 0 | 1 | 0 | 0 | 1 | |
| P[3]: 7 | 1 | 0 | 1 | 0 | 0 | 1 | |
| P[4]: 9 | 1 | 0 | 1 | 0 | 0 | 1 |
(key值为5,p数组:10,5,2,7,9)
出口:1.当n==0时,判断p[0]==key?
2.当key==0时,真
3.当p[i]>key时,不选p[i]
4.否则,选择p[i]||不选p[i]
#include<stdio.h>
#include<string.h>
int solve_dp(int *p,int n,int key);
int solve_dp(int *p,int n,int key)
{
int i,j;
int**opt=malloc(n*sizeof(int));//定义二级指针(行数)
for(i=0; i<n; i++) //(每行都有多少列)
opt[i]=malloc((key+1)*sizeof(int *)); //注意二维数组的动态开辟方法
for(i = 0; i <= key; i++)
opt[0][i] = 0; //第一行(对应递归版n为0时的情况)
for(i = 0; i < n; i++)
opt[i][0] = 1; //当key为0时,该列都为1
opt[0][p[0]]=1;// 但如果p[0]==key,则该值为1(覆盖掉原来的0,上面注释为红色部分)
for(i=1; i<n; i++)//遍历p数组,以及二维数组的列
for(j=1; j<=key; j++)//二维数组的行
{
if(p[i]>j)
opt[i][j]=opt[i-1][j];
else
opt[i][j]=opt[i-1][j-p[i]]||opt[i-1][j];
}
for(i=0;i<n;i++)
{
for(j=0;j<=key;j++)
printf("%d ",opt[i][j]);
putchar('\n');
}//打印二维数组(用于验证)
j=opt[n-1][key];//保存结果
free(opt);//及时释放掉二维数组
return j;
}
int main()
{
int n,i,key;
printf("Input the length\n");
scanf("%d",&n);
int *p=malloc(n*sizeof(int));
printf("Input the array\n");
for(i=0; i<n; i++)
scanf("%d",&p[i]);
for(i=0; i<20; i++)
{
printf("Input the test number\n");
scanf("%d",&key);
if(solve_dp(p,n,key))
printf("True\n");
else
printf("False\n");
}
return 0;
}