洛谷 P1466 集合 Subset Sums
https://www.luogu.org/problemnew/show/P1466
JDOJ 1681: Subset Sums
https://neooj.com:8082/oldoj/problem.php?id=1681
Description
对于从1 到N 的连续整集合合,能划分成两个子集合,且保证每个集合的数字和是相等的.
举个例子,如果N=3,对于{1,2,3}能划分成两个子集合,他们每个的所有数字和是相等的:
{3} and {1,2}
这是唯一一种分发(交换集合位置被认为是同一种划分方案,因此不会增加划分方案总数)
如果N=7,有四种方法能划分集合{1,2,3,4,5,6,7},每一种分发的子集合各数字和是相等的:
{1,6,7} and {2,3,4,5} {注 1+6+7=2+3+4+5}
{2,5,7} and {1,3,4,6}
{3,4,7} and {1,2,5,6}
{1,2,4,7} and {3,5,6}
给出N,你的程序应该输出划分方案总数,如果不存在这样的划分方案,则输出0.程序不能预存结果
直接输出.
Input
输入文件只有一行,且只有一个整数N
Output
输出划分方案总数,如果不存在则输出0.
Sample Input
7
Sample Output
4
HINT
N <= 39
动归。
最终可以转化成背包问题。为什么呢?
因为题目中让你把集合中元素分成两个权值相等的部分,也就是说元素和相加除以2就是背包容量,我们的状态f[i][j]就设置为第i个元素装满容量为j的背包的方案数目。
好了,现在这个题目就变成了01背包问题。
只不过是求方案罢了。
先上代码,再强调一下细节。
AC CODE:
#include<cstdio> using namespace std; int n,t,f[40][1000]; int main() { scanf("%d",&n); t=(n+1)*n/2; if(t%2) { printf("0"); return 0; } else t/=2; f[1][1]=1; for(int i=2;i<=n;i++) for(int j=0;j<=t;j++) { if(j>i) f[i][j]=f[i-1][j]+f[i-1][j-i]; else f[i][j]=f[i-1][j]; } printf("%d",f[n][t]); return 0; }
首先要判断当前集合的元素和能否被2整除,不能的话还玩个头。
然后要设定初值,f[1][1]=1,我想不用多说。
PS;有人也把f[1][0]设置为0了,我本人不是很理解,事实证明删去也毫无问题。
然后开始DP,注意t要除以2。
我们在每次状态转移之前要先加判断,如果可用空间比当前元素要大的话才可以把选和不选两种可能加到一起,否则就只能不选。
选和不选的问题或者这段代码看不懂的请自行补习01背包。
然后这道题就可以AC 了。