Description
对于从1到N (1 <= N <= 39) 的连续整数集合,能划分成两个子集合,且保证每个集合的数字和是相等的。举个例子,如果N=3,对于{1,2,3}能划分成两个子集合,每个子集合的所有数字和是相等的:
{3} 和 {1,2}
这是唯一一种分法(交换集合位置被认为是同一种划分方案,因此不会增加划分方案总数) 如果N=7,有四种方法能划分集合{1,2,3,4,5,6,7},每一种分法的子集合各数字和是相等的:
{1,6,7} 和 {2,3,4,5} {注 1+6+7=2+3+4+5}
{2,5,7} 和 {1,3,4,6}
{3,4,7} 和 {1,2,5,6}
{1,2,4,7} 和 {3,5,6}
给出N,你的程序应该输出划分方案总数,如果不存在这样的划分方案,则输出0。
Input
有多组测试数据。
对于每组测试数据,输入一个整数n。
Output
对于每组测试数据,输出划分方案总数,如果不存在则输出0。
Sample Input
7
Sample Output
4
**题解:**要把这些数分成两个求和后相等的集合,那么首先计算求和后的值,然后计算一个集合的求和是多少。
可以发现,这是一个求方案数的动态规划【递推?】
其实就是一个递推。。。。只有求和为0时方案数为1,一个数字0,求和值为j即用1到n每一个数加入到集合中时的方案数之和
诶现在一看真是非常的简单了orz
最后注意dp【half】结果其实是存在重复的,因为集合A是1 2 4 7 ,集合B是3 5 6与集合A是3 5 6 而集合B是1 2 4 7是一模一样的,要除2
#include<stdio.h>
#include<string.h>
int main()
{
long long dp[600];
int i,n,j;
while(scanf("%d",&n)!=EOF)
{
memset(dp,0,sizeof(dp));
int sum=n*(n+1)/2;///等差数列前n项求和公式
int half=sum/2;
if(sum%2!=0)///因为要把sum总和分成两个相等求和的集合,因此sum总和必须能被2整除否则一个方法也没有
{
printf("0
");
continue;
}
dp[0]=1;///当输入0时肯定只有一种分法
for(i=1;i<=n;i++)///dp第i个数字
{
for(j=half;j>=i;j--)
{
dp[j]=dp[j]+dp[j-i];///达到每一个总和的方法,都是减去这个数所继承的上一个数的方法个数
}
// for(int k=half;k>=0;k--)
// {
// printf("%d
",dp[k]);
// }
// printf("====
");
}
printf("%lld
",dp[half]/2);///此处算上的是两个集合交换位置,有重复的,要除2
}
return 0;
}
幂集
Description
所谓幂集,就是原集合中所有子集构成的集合,例如集合{1,2,3}的幂集为{{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3},{空集}}。现有一个元素个数为n的集合A={a1,a2,a3,a4…an},请求出集合A的幂集中有多少个 子集的和等于整数x。
Input
有多组测试数据处理到文件结束,每组数据第一行包含两个整数n(n<=30)和x(x>0);
第二行有n个整数,0<ai<300,代表集合A中的元素。
Output
输出集合A的幂集中子集和为x的子集的个数。
Sample Input
5 6
1 2 2 4 5
3 7
1 2 3
Sample Output
3
0
Hint
第一组样例中{1,5},{2,4},{2,4}这三个子集的和都等于6,所以答案为3.
**题解:**把这两题放一起因为是一样的,上一题中的背包容量是所有数求和的一半。这题中其实只需要求得集合内所有数能组成求和为多大的背包,即可像上一题一样得到用01背包计算每种求和的方法数,最后输出求和为x时的方法数,即是结果
#include<stdio.h>///和1541的 集合划分 是一样的,把数字当一个物品,然后01背包
#include<string.h>
int main()
{
int a[39],i,j,x,n,dp[9005];///有30个数,每个数不超过300,dp的单位是数值大小,因此最大值是9000
while(scanf("%d%d",&n,&x)!=EOF)
{
int sum=0;///这里所求的子集数量,其实就是所有元素能加和得到方法数
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];///求的最大dp值
}
memset(dp,0,sizeof(dp));///记得清空
dp[0]=1;///当求和为0时,只有空集是这样的,所以只有1个子集(输入的元素大于0)
for(i=0;i<n;i++)
{
for(j=sum;j>=a[i];j--)
{
dp[j]=dp[j]+dp[j-a[i]];
}
}
printf("%d
",dp[x]);///只需要当总和为x时的方法数
}
return 0;
}