等和的分隔子集

晓萌希望将 11 到 NN 的连续整数组成的集合划分成两个子集合,且保证每个集合的数字和是相等。例如,对于 N=3N=3,对应的集合 \{1,2,3\}{1,2,3} 能被划分成 \{3\}{3} 和 \{1,2\}{1,2} 两个子集合.

这两个子集合中元素分别的和是相等的。

对于 N=3N=3 ,我们只有一种划分方法,而对于 N=7N=7 时,我们将有 44 种划分的方案。

输入格式

输入包括一行,仅一个整数,表示 N(1 \le N \le 39)N(1N39)的值。

输出格式

输出包括一行,仅一个整数,晓萌可以划分对应 NN 的集合的方案的个数。当没法划分时,输出 00

样例输入

7

样例输出

4
/*
刚开始看到这道题时, 我第一想到的时枚举所有子集,
然后累加判断, 但是粗略估算了下时间, 2^40 约等于 10^12(2^10 = 1024 ~ 2^10)
绝对会TLE, 并且想不到另外的好的方法, 也不会剪枝...
后来看到群里有人讨论这道题, 说是dp, 我当时粗略的草算了下, 貌似是...
后来又想起来这道题, 做了下, 并且和背包差不多
dp[pos, rest] 表示在当前位置上, 组成rest的方案数
状态转移方程: dp[pos, rest] = dp[pos - 1, rest] + dp[pos - 1, rest - pos]
当剩余为rest时pos为当前值, 组成方案数可以分成两个部分...
=============================================
做dp还是需要分解问题, 看清问题,
然后确定是dp后, 要拆分问题, 寻找子状态, 判断边界条件, 确定数据范围
*/
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
using namespace std;
const int maxn = 1800;
typedef long long ll;
ll dp[maxn];
int main()
{
    dp[0] = 1;
    int N;
    scanf("%d", &N);
    int sum = N * (N + 1) / 2;
    if(sum & 1){
        printf("0\n");
        return 0;
    }
    int goal = sum / 2;
    for(int pos = 1; pos <= N; ++pos)
        for(int rest = goal; rest > 0; --rest){
            // printf("%d, %d\t", pos, rest);
            if(rest < pos)
                dp[rest] = dp[rest];
            else
                dp[rest] = dp[rest] + dp[rest - pos];
        }
        /* 更好的简写
        for(int rest = goal; rest >= pos; --rest)
            dp[rest] += dp[rest - pos];
        */
    printf("%lld\n", dp[goal] / 2);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013482363/article/details/79406893
今日推荐