那么仔细分析一下这个题是一个0/1背包的问题,将一个集合划分成 两个“元素总和相等”的集合,设原集合的元素总和为sum,则划分后的集合的元素综合都为sum/2。那么我们可以把sum/2看成背包的容量,原集合中的数字看为物品的重量及价值(这里价值维度可以淡化,就像装箱问题)。则原问题转化为 “从原集合中选出n个物品,使这n个物品恰好放满容量为sum/2的背包的方案总数”。
设分成的子集为set1,set2,sum=(n*(n+1)div 2)div 2。设num[i,j]表示取前i个数,使set1总数和为j的方案数.第i个数的值为i,根据是否取第i个数就有:
num[i,j]=num[i-1,j]+num[i-1,j-i] j-i>=0
num[i,j]=num[i-1,j] j-i<0
则最后的结果为num[n,sum] div 2。
【代码】:
//dp[i][j]表示 前 I个和为 J的可能有几种
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
int n,sum;
long long num[40][780];
int main()
{
freopen("subset.in","r",stdin);
freopen("subset.out","w",stdout);
cin>>n;
if((n*(n+1)/2)%2)
{
cout<<"0"<<endl;
return 0;
}
sum=n*(n+1)/4;
memset(num,0,sizeof(num));
num[1][1]=1;
num[1][0]=1;
for(int i=2;i<=n;i++)
{
for(long j=0;j<=i*(i+1)/2;j++)
if(j>=i)
num[i][j]=num[i-1][j]+num[i-1][j-i];
else
num[i][j]=num[i-1][j];
}
cout<<num[n][sum]/2<<endl;
return 0;
}