P1466 [USACO2.2]集合 Subset Sums
这也能动规,而且还是01背包.
dp[i][j]表示从1~i的数字中选择,能够得到和为j的方案数.那么dp[i][0]=1.对于每个数字选或者不选,有:
dp[i + 1][j] = dp[i][j] + dp[i][j - i],这里的i相当于背包问题中物体i的重量,这里的"重量"就是i自身.
压维,dp[j] += dp[j - i].
此时½∑i=1n即为选取和为两个子集的和的方案数.注意到这里面是有重复部分的,如{1,2,3}被拆分为方案①{1,2},{3}和方案②{3},{1,2}.
输出该方案数除以二即可.
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> using namespace std; int n, M; long long dp[800]; int main(){ cin >> n; M = n * n + n; if((M / 2) & 1){ puts("0"); return 0; } M /= 2; // sum 1 to n dp[0] = 1; for(int i = 1; i <= n; i++) for(int j = M / 2; j >= i; j--) dp[j] += dp[j - i]; cout << dp[M / 2] / 2; return 0; }
怎么看出来这是一道动规题?缩小范围,为什么这是一道背包问题?
因为问题可以转化为取还是不取的问题,如果以搜索的视角看,还有存储结果的需求.
如果说取还是不取,前一段时间做的搜索几乎一直在做这件事情,这两者之间的关系太紧密了.