zoj 3747 Attack on Titans 带限制条件的计数递推dp

2019-04-14 18:37发布

本来以为是组合数学的题,结果被一个至多一个至少搞得无从下手,后来学习到可以将至少转化为至多:在n个元素里面至少有m个a等价于在n个元素里面至多有n个a-在n个元素里至多有n-m个a。另外就是比较常见的取模问题:可简单的认为做一次运算就取一次模。减法运算为了防止出现负数,在模完后还要再加上模数再取一次模。#include using namespace std; const long long M = 1000000007; const long long N = 1000000; long long s[N][3]; long long solve(long long n, long long m, long long k){ long long sum; s[0][0] = 1, s[0][1] = 0, s[0][2] = 0; for(int i = 1; i <= n; i ++){ sum = (s[i - 1][0] + s[i - 1][1] + s[i - 1][2]) % M; s[i][2] = sum; if(i <= m) s[i][0] = sum; else if(i == m + 1) s[i][0] = (sum - 1) % M; else s[i][0] = (sum - s[i - m - 1][1] - s[i - m - 1][2]) % M; if(i <= k) s[i][1] = sum; else if(i == k + 1) s[i][1] = (sum - 1) % M; else s[i][1] = (sum - s[i - k - 1][0] - s[i - k - 1][2]) % M; } return (s[n][0] + s[n][1] + s[n][2]) % M; } int main(){ long long n, m, k; long long ans1, ans2; while(cin >> n >> m >> k){ ans1 = solve(n, n, k); ans2 = solve(n, m - 1, k); cout << ((ans1 - ans2) % M + M) % M << endl; } return 0; }

二周目,这次对一些事情进行更详细地说明,首先就是最多最少的转换上,题目虽然说最少m个,但其实这是两个条件——大于等于m且小于等于n,借助这个才可以推出最少m个时的组合数 = 最多n个的组合数 - 最多m - 1个的组合数。第二点,对于dp数组的初始化,显然对于第一个位置,各个兵团都只有一种排列方法。第三点,取模,ans1 可能小于ans2,所以要加上M再取模,ans1 - ans2 的绝对值可能大于M,所以要先对他们的差取模然后加上M再一同取模