https://ac.nowcoder.com/acm/contest/881/E
题意:对于一个长度为
2
(
n
+
m
)
2(n+m)
2(n+m)的仅由
A
A
A和
B
B
B组成的序列,能将其分成
(
n
+
m
)
(n+m)
(n+m)个长度为
2
2
2的子序列,其中
n
n
n个是"
A
B
AB
AB",
m
m
m个是"
B
A
BA
BA"。求对于给定的
n
n
n和
m
m
m,有多少种合法的序列。
DP,令
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示该序列前缀
i
+
j
i+j
i+j长度有
i
i
i个
A
A
A和
j
j
j个
B
B
B的方案数,因为AB的数量
n
n
n和BA的数量
m
m
m是固定的,同时A和B的数量也是固定的(都是
n
+
m
n+m
n+m个),所以可以知道前缀
i
+
j
i+j
i+j中A和B的数量也就可以求得后缀
2
(
n
+
m
)
−
(
i
+
j
)
2(n+m)-(i+j)
2(n+m)−(i+j)中A和B的数量,而每次需要A时,贪心地先用AB的,AB的用完了再用前面出现过B的BA,需要B时同理。
每次枚举下一个位置是A和B(转移到
f
[
i
+
1
]
[
j
]
f[i+1][j]
f[i+1][j]和
f
[
i
]
[
j
+
1
]
f[i][j+1]
f[i][j+1]),通过剩余AB和BA的数量判断是否可以转移。
要注意因为是多组数据,
f
f
f数组又比较大,对于每组数据不能直接用memset清空,否则会超时。
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#define MOD 1000000007
using namespace std;
int n,m;
long long f[2010][2010];
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=0;i<=n+m;i++)
for(int j=0;j<=n+m;j++)
f[i][j]=0;
f[0][0]=1;
for(int i=0;i<=n+m;i++)
{
for(int j=0;j<=n+m;j++)
{
if(n-i+j>0)
f[i+1][j]=(f[i+1][j]+f[i][j])%MOD;
if(m-j+i>0)
f[i][j+1]=(f[i][j+1]+f[i][j])%MOD;
}
}
printf("%lld\n",f[n+m][n+m]);
}
return 0;
}