problem
有1~N这N个数,蕊蕊希望将这N个数划分成两个子集合,且保证每个集合的数字和是相等的。比如N=3,对应的集合{1,2,3}能被划分成{3}和{1,2}
这两个子集合中元素分别的和是相等的。
对于N=3,我们只有一种划分方法,而对于N=7时,我们将有4种划分的方案。
输入格式
输入包括一行,仅一个整数,表示N(1≤N≤40)的值。
输出格式
输出包括一行,仅一个整数,蕊蕊可以划分对应 N 的集合的方案的个数。当没法划分时,输出 0。
样例输入
7
样例输出
4
思路
经典的每一项选择与不选择问题
这里虽然N不大,到40,但是如果用DFS会爆:(大概到27)
#include<bits/stdc++.h>
using namespace std;
int n;
int goal;
int ans;
void dfs(int a,int num,int sum,int limit)
{
//num是选择数量 sum是和 limit是应该选择数量
if(num==limit){
if(sum==goal) ans++;
return ;
}
for(int i=a+1;i<=n;++i)
if(sum+i<=goal)
dfs(i,num+1,sum+i,limit);
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
if(n==1||n==2) cout<<0<<endl;
else if(n==3) cout<<1<<endl;
else{
ans=0;
goal=n*(n+1)/4;
for(int i=2;i<n-1;++i)//选i个
{
dfs(0,0,0,i);
//cout<<i<<' '<<ans<<endl;
}
cout<<ans/2<<endl;
}
return 0;
}
考虑DP
看成一个 0,1 背包,背包容量看成是sum/2,最后求得是正好装满背包的方案数
核心代码
dp[0][0]=1;
for(int i=1;i<=n;++i){//前i个中去选
for(int j=goal;j>=i;--j){
dp[i][j]=dp[i-1][j]+dp[i-1][j-i];
}
for(int j=i-1;j>=0;--j){
dp[i][j]=dp[i-1][j];
}
}
注意最后输出要除2(每个方案两个子集)
cout<<dp[n][goal]/2<<endl;
代码示例
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=500;
int n,goal;
ll dp[maxn][maxn];
int main()
{
ios::sync_with_stdio(false);
cin>>n;
if((n*(n+1)/2)%2) cout<<0<<endl;
else{
goal=n*(n+1)/4;
/*
for(int i=1;i<=n;++i){//前i个
for(int j=goal;j>=i;--j){
dp[i][j]=dp[i-1][j]+dp[i-1][j-i];
}
for(int j=i-1;j>=0;--j){
dp[i][j]=dp[i-1][j];
}
}
*/
dp[0][0]=1;
for(int i=1;i<=n;++i){//前i个中去选
for(int j=goal;j>=i;--j){
dp[i][j]=dp[i-1][j]+dp[i-1][j-i];
}
for(int j=i-1;j>=0;--j){
dp[i][j]=dp[i-1][j];
}
}
cout<<dp[n][goal]/2<<endl;
}
return 0;
}