2019牛客多校第一场
E:ABBA(贪心+DP)
题意:就是有\(n\)个"AB",\(m\)个"BA",问能结合成多少个序列.这个要求是AB和BA的顺序不变,即A和B的相对位置不变.我们要讨论一下什么才是合法的状态.
- 贪心:
- 假设只有\(n\)个AB,合法情况是每个B前面要有\(1 ...n\)个A
- 假设除了有AB,还有\(m\)个BA,那每个B前面可以有超过\(n\)个A,但是第一个B前面还是要有\(1...n\)个A.否则就会使BA类型的某个B后面没A.
- B与后面的A可以构成BA,相当于抵消了一个A,那下一个B前面就只需要有\(1...n\)未被抵消的A.
- 所以A-B小于等于\(n\)是合法的.当A-B等于\(n\),意味着最后一个只能是A,因为如果最后一个是B,那B前面就有\(n+1\)个未被抵消的A.
- A前面有多少个B也是同理.
DP
\(dp[i][j]\),\(i\)代表A的个数,\(j\)代表B的个数.\(dp\)值代表合法方案.
初始化,按照上述每个A前面有\(1...m\)个B是合法,每个B前面有\(1...n\)个A是合法
\(dp[i][0]=dp[0][j]=1\)
不考虑那么多,可以得出
\[ dp[i][j]=dp[i-1][j]+dp[i][j-1] \]但是要排除非法的情况
- \(0\leq j-i\leq m\)和\(0\leq i-j \leq n\) 是合法情况
- 注意\(=m\)和\(=n\)和\(=0\)的情况
还是想说,浩哥天下第一
代码
#include <bits/stdc++.h> #define ll long long using namespace std; const ll mod = 1e9 + 7; const ll N = 1e5 + 10; ll dp[2010][2010]; int main() { ll n, m; while (scanf("%lld%lld", &n, &m) != EOF) { ll cnt = (n + m); for (int i = 0; i <= cnt; i++) for (int j = 0; j <= cnt; j++) dp[i][j] = 0; for (ll i = 0; i <= cnt; i++) { dp[i][0] = dp[0][i] = 1; } for (ll i = 1; i <= cnt; i++) { for (ll j = 1; j <= cnt; j++) { if (i == j) { if (n) dp[i][j] = (dp[i][j] + dp[i][j - 1]) % mod; if (m) dp[i][j] = (dp[i][j] + dp[i - 1][j]) % mod; } else if (j > i) { if (j - i < m) dp[i][j] = ((dp[i][j] + dp[i - 1][j]) % mod + dp[i][j - 1]) % mod; else if (j - i == m) dp[i][j] = (dp[i][j] + dp[i][j - 1]) % mod; } else if (i > j) { if (i - j < n) dp[i][j] = ((dp[i][j] + dp[i - 1][j]) % mod + dp[i][j - 1]) % mod; if (i - j == n) dp[i][j] = (dp[i][j] + dp[i - 1][j]) % mod; } //cout<<i<<" "<<j<<" "<<dp[i][j]<<endl; } } printf("%lld\n", dp[cnt][cnt] % mod); } return 0;