描述
对于从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。程序不能预存结果直接输出(不能打表)。
格式
PROGRAM NAME: subset
INPUT FORMAT:
(file subset.in)
输入文件只有一行,且只有一个整数N
OUTPUT FORMAT:
(file subset.out)
输出划分方案总数,如果不存在则输出0。
SAMPLE INPUT
7
SAMPLE OUTPUT
4
忘记的速度真快,思想变钝的速度也好快,这么简单的DP都没有即时看出来,题不能停啊!
状态转移方程:
①j-i>=0:dp[i][j]=dp[i-1][j]+dp[i-1][j-i]
②j-i<0:dp[i][j]=dp[i-1][j];
/*
ID: your_id_here
PROG: subset
LANG: C++
*/
#include <cstdio>
#include <cstring>
using namespace std;
int i,j,n,sum;
unsigned int dp[41][2005];//注意结果在int内,则中间值在unsigned int内
int main() {
freopen("subset.in","r",stdin);
freopen("subset.out","w",stdout);
while(1==scanf("%d",&n)) {
sum=(n+1)*n/2;
if(sum&1)//如果总和为奇数则不可分
printf("0\n");
else {
memset(dp,0,sizeof(dp));
sum>>=1;
dp[1][0]=dp[1][1]=1;
for(i=2;i<=n;++i)//变量i表示当前有i个数字
for(j=0;j<=sum;++j) {
dp[i][j]=dp[i-1][j];//i个数字凑成j的方案数来自于①i-1个数字凑成j的方案数
if(j-i>=0)
dp[i][j]+=dp[i-1][j-i];//若j-i>=0:i个数字凑成j的方案数又来自于②i-1个数字凑成j-i的方案数
}
printf("%d\n",dp[n][sum]>>1);//由于计算出所有的集合方案,结果需要除以2
}
}
return 0;
}