2019牛客暑期多校训练营(第一场)E.ABBA

it2022-05-05  95

https://ac.nowcoder.com/acm/contest/881/E

题意:构造一个长度为2*(n+m)的AB串,使这个串可以划分成n和“AB”的子序列和m个“BA”的子序列,求方案数量

思路:

首先注意:子序列不是子串 只需要相对位置正确 不需要是连续的

确定状态:求什么就设什么,设dp[i][j]为前i个A+前j个B可以组合的合法字符串的数量,那么我们最终要求的dp[n+m][n+m]就是本题的解

对于一个合法的字符串,明确一个性质:

前n个A必定是“AB”的A,同理,可以得到前m个B必定是“BA”的B

可以简单证明一下:如果前n个A里,有一个是“BA”的A,那么这个A后边肯定还存在一个A可以给这个B组合在一起形成“BA”,那么这个A就从原本的“BA”里A变成“AB”里的A,这样就可以得出前n个A都是“AB”的A的结论

之后就是枚举下一个字符是A 还是B

下一个字符放A:

如果i < n,由前n个A必定是“AB”的A可知可以放A

如果i >= n,这个时候就要看B的个数了 ,要确保这个A能给前面的B当"BA"用,那么当前"BA"里需要的A是min(j, m)。所以i - n < min(j,m)成立时,也可以放A。

解释一下为什么是min(j,m), 如果j < m,min(j,m) = j,那么前j个B需要j个A构成“BA”,如果j > m,min(j,m) = m,前m个B都需要m个A构成“BA”。

下一个字符放B:

和放A同理。

如果j < m,由前m个B必定是“BA”的B可知可以放B

如果j >= m,这个时候就要看A的个数了 ,要确保这个B能给前面的A当"AB"用,那么当前"AB"里需要的B是min(i, n)。所以j - m < min(i,n)成立时,也可以放B。

代码

#include <iostream> #include <cstring> #include <algorithm> #include <string> #define ll long long #include <set> using namespace std; const int Max = 2010; const int mod = 1e9+7; ll n,m; ll dp[Max][Max]; int main(){ while(cin>>n>>m){ int s = n+m; for(int i = 0 ; i <= s; i++){ for(int j = 0 ; j <= s; j++){ dp[i][j] = 0; } } dp[0][0] = 1; for(ll i = 0 ; i <= s; i++){ for(ll j = 0 ; j <= s; j++){ //放A if(i < n || (i-n) < min(j,m)){ dp[i+1][j] = (dp[i+1][j] + dp[i][j])%mod; } //放B if(j < m || (j-m) < min(i,n)){ dp[i][j+1] = (dp[i][j+1] + dp[i][j])%mod; } } } cout<<dp[s][s]<<"\n"; } return 0; }

 


最新回复(0)