经过了许久的学习,就应该将所学沉淀下来。
那么今天小编记录的,是一道挺有趣的编程题——子集的和。
这道题可以用动态规划(DP)来解决。这次我做的是新学的动态规划类型的题:子集的和……
题目描述
对于从1到N (1 <= N <= 39) 的连续整数集合,能划分成两个子集合,且保证每个集合的数字之和是相等的。
举个例子,如果N=3,对于{1,2,3}能划分成两个子集合,他们每个的所有数字和是相等的:
{3} and {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。
输入
第1行:一个整数N
输出
第1行:输出划分方案总数,如果不存在则输出0。
样例输入
7
样例输出
4
我们拿到这道题,首先应该判断它应该用哪种算法(思想)来做。
因为这道题的方案数量只与局部最优有关,而且可以划分阶段,所以我选择通过动态规划做。
那么怎么利用动态规划呢?
动态规划的本质其实就是记忆化搜索,但动归是以递推的形式呈现的,动归有以下一些步骤。
1.利用数组表示状态
2.赋初值
3写出动态转移方程式
4利用求解
该题就是一个例子了!
#include<cstdio>
#include<algorithm>
using namespace std;
int n,f[40][808],sum;//利用f数组储存状态f[i][j]代表着用前i个整数中加和为j的方案数
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{f[i][0]=1;sum+=i;} //如果前i个整数中加和为0,只有1个方案,用sum存总和
if(sum&1) //其实就是sum%2==1
{
printf("0");
return 0; //当总和都是奇数时,是不能分成相等的两个整数的
}
for(int i=1;i<=n;i++) //通过i,j的范围,找f[i][j]
for(int j=n*(n+1)/4;j>0;j--)
if(j>=i)f[i][j]=f[i-1][j]+f[i-1][j-i]; //如果要加第i个数,那么i-1,加和要减去第i个数的值,也就是减去i;如果不加,只用i-1,。两种情况应该相加
else f[i][j]=f[i-1][j]; //如果j<i,加和就不能减第i个数了,只有1种情况
printf("%d",f[n][n*(n+1)/4]); //前n个整数中加和为n*(n+1)/4的方案数即为解
return 0;
}