题意:问有多少个有(n + m)个A和(n + m)个B的字符串可以凑出n个AB和m个BA。
思路:首先贪心的发现,如果从前往后扫,遇到了一个A,优先把它看成AB的A,B同理。这个贪心策略用邻项交换很好证明。之后,我们设dp[i][j]为填了i个A和j个B的字符串不违法的方案数。什么叫不违法呢?有一些方案是一定不可以凑出n个AB和m个BA的,比如如果i - n > j了就不行:现在已经有i个A,其中有n个A用来充当AB的A,那么剩下的A只能去充当BA的A,但是假如现在你的B的个数j小于的BA的A的个数,那么你后面有多少个B都于事无补了,一定会少一个BA。所以状态必须满足两个不等式:i - n <= j, j - m <= i。只要满足这两个条件,状态直接转移就可以了。
代码:
#include <bits/stdc++.h> #define LL long long using namespace std; const int maxn = 2010; const LL mod = 1e9 + 7; LL dp[maxn][maxn]; int n, m, x, y; bool valid(int A, int B) { return (B >= A - n) && (A >= B - m); } void solve() { for (int i = 0; i <= n + m; i++) { for (int j = 0; j <= n + m; j++) { dp[i][j] = 0; if(i == 0 && j == 0) dp[i][j] = 1; if(!valid(i, j)) continue; if(j) dp[i][j] += dp[i][j - 1]; if(i) dp[i][j] += dp[i - 1][j]; dp[i][j] %= mod; } } printf("%lld ", dp[n + m][n + m]); } int main() { while(~scanf("%d%d", &n, &m)) { solve(); } }