POJ 3046题目大意如下:
指定蚂蚁家族有T个,每个家族的蚂蚁数量最多为100只,最少当然为1了,总共有A只蚂蚁,每只蚂蚁唯一的区别就是它所对应的家族编号,相同编号的蚂蚁看成没有差别。现在将这些蚂蚁分成不同的集合,指定每次分割是的集合大小(每次分割出来的集合大小都是统一的)。现在指定集合大小的范围,求出对于不同集合大小的分割方法的总和。
这是一个递推问题当然也是DP,所以重要的不是自己去模拟分类,而是找出一个合理的关系式,再加上合理的初始化,就可以解决问题了。如果有必要,可以在空间使用率上再作优化。这里我们这样定义DP表达式:dp[i+1][j]表示使用前i个家庭的蚂蚁分解成集合长度为j的所有方法数。那么就有以下关系:
dp[i + 1][j] = Sigma(0, min{num[i + 1], j})dp[i][j - k] = Sigma(0, min(num[i + 1], j - 1})dp[i + 1][j - 1 - k] + dp[i][j] = dp[i + 1][j - 1] + dp[i][j];
然后就是空间的优化了,因为题目给的数据太大,数组的花销有点大,但是这里的第一维其实只需要2就可以了因为总是在i+1和i之间转换。说到这里,结果已经出来了,但遗憾的是,代码居然wrong 了,看了好久没看出什么端倪来,和网上其他人对比了一下,我感觉一样。下面虽然会贴上没有AC的代码,然而今天这篇就当作算法说明吧,可以参考修改,主要算法还是没有错的,代码如下:
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; const int maxn1 = 1000; const int maxn2 = 100000; int ants[maxn1 + 2]; int dp[2][maxn2 + 1]; int kind[maxn1 + 2] = {0}; int T, A, S, B; void solve() { memset(dp, 0, sizeof(dp)); for (int i = 1; i <= T; i++) kind[i] = kind[i - 1] + ants[i]; dp[0][0] = dp[1][0] = 1; for (int i = 0; i < T; i++) { for (int j = 1; j <= kind[i + 1] && j <= B; j++) { if (j - 1 - ants[i + 1] >= 0) dp[(i + 1) % 2][j] = (dp[(i + 1) % 2][j - 1] + dp[i % 2][j] - dp[i % 2][j - 1 - ants[i + 1]]) % 1000000; else dp[(i + 1) % 2][j] = (dp[(i + 1) % 2][j - 1] + dp[i % 2][j]) % 1000000; } } int t = T % 2, ans = 0; for (int i = S; i <= B; i++) ans = (ans + dp[t][i]) % 1000000; printf("%d\n", ans); } int main(int argc, const char * argv[]) { // insert code here... int data; scanf("%d%d%d%d", &T, &A, &S, &B); for (int i = 0; i < A; i++) { scanf("%d", &data); ants[data]++; } solve(); return 0; }