https://ac.nowcoder.com/acm/contest/881/E
链接:https://ac.nowcoder.com/acm/contest/881/E
来源:牛客网
题目描述
Bobo has a string of length 2(n + m) which consists of characters `A` and `B`. The string also has a fascinating property: it can be decomposed into (n + m) subsequences of length 2, and among the (n + m) subsequences n of them are `AB` while other m of them are `BA`.
Given n and m, find the number of possible strings modulo (109+7)(109+7).
输入描述:
The input consists of several test cases and is terminated by end-of-file.
Each test case contains two integers n and m.
* 0≤n,m≤1030≤n,m≤103
* There are at most 2019 test cases, and at most 20 of them has max{n,m}>50max{n,m}>50.
输出描述:
For each test case, print an integer which denotes the result.
示例1
输入
复制
1 2
1000 1000
0 0
输出
复制
13
436240410
1
这道题,打眼一看没有头绪,其实细想五分钟,就会仍然没有头绪,得细想20分钟
题意:
输入n,m,表示有n个“AB”串,m个“BA”串
问有多少种长度为2*(n+m)的字符串,可以提取出n个“AB”串,m个“BA”串
首先,设有个字符串符合这个要求,则需要用贪心的方法进行匹配
设n=2,m=1
则很显然,
AAABBB是不符合条件的,
因为n为2,最多只能有两个A在未配对B的前面
同样 BBAAAB也是不符合条件的
m为1,最多只能有一个B在未配对的A前面
ABBABA符合条件
字符串为:
A | B | B | A | B | A |
1 | 2 | 3 | 4 | 5 | 6 |
则利用贪心的思想,先紧着AB匹配,(如果先紧着BA匹配也一样)
那么,我们先紧着AB进行匹配(想紧着BA进行匹配也没毛病)
从前缀字串中A、B两个字符的个数
前缀子串 | A的个数 | B的个数 | A的个数减去B的个数() | 删除可以匹配AB或者BA的字符后 |
A | 1 | 0 | 1 | “A” |
AB | 1 | 1 | 0 | “” |
ABB | 1 | 2 | -1 | “B” |
ABBA | 2 | 2 | 0 | “BA” |
ABBAB | 2 | 3 | -1 | “B” |
ABBABA | 3 | 3 | 0 | “” |
所以我们利用A、B字符个数的差值(A-B)来表示该前缀字符串中剩余的A或者B(也就是状态转移方程中的状态)
让A当作 1 ,B当作 - 1
则在ABBABA字符串中,其前缀和表示前缀字串的A、B的差值:
A | B | B | A | B | A |
1 | 0 | -1 | 0 | -1 | 0 |
我们通过枚举差值来转移状态
则k位的A-B差值为x的字符串的个数
可以通过k-1位的状态获得:
k-1位串中AB差值为x-1的个数通过增加一个A,使得k位串中A-B的差值为x
加上k-1位串中AB差值位x+1的个数铜鼓增加一个B,使得k位串中A-B的差值位x
即:dp[ k ] [ x ] = dp [ k - 1 ] [ x - 1 ] + dp [ k - 1 ] [ x + 1 ]
x可能是负数,所以在下面代码实现得过程中,进行了一次把数轴右移了1500,解决了状态为负数情况下得计数问题
接下来寻找上下界:
通过上图知:在计算前缀串A-B个数差值过程中,必须符合( A - B ) <= n && fabs( A - B )<=m ,所以有:-m <= ( A - B ) <= n
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
const ll mod=1e9+7;
ll dp[4010],dp1[4010];
int main()
{
int n,m;
while(cin>>n>>m)
{
int t=2*(n+m);
m=-m;
memset(dp,0,sizeof(dp));
memset(dp1,0,sizeof(dp1));
dp[1500]=1;///为了表示负状态,数轴右移1500,1500代表着零,起始状态AB的差值是0,且只有一种排列方式:“” 空字符串
for(int k=1;k<=t;k++)///递推从k-1递推到k
{
for(int j=1500+m;j<=1500+n;j++)///状态范围
{
dp1[j]+=dp[j-1];选择A
dp1[j]+=dp[j+1];选择B
dp1[j]%=mod;
}//+1
for(int j=1500+m;j<=1500+n;j++)///滚动数组
{
dp[j]=dp1[j];
dp1[j]=0;
}
}
cout<<dp[1500]<<endl;///状态为0,代表AB个数一样
}
return 0;
}
写的真烂,自己都看不下去