nowcoder—2019牛客暑期多校训练营(第一场) E题 ABBA (动态规划前缀方案和)

22 篇文章 0 订阅
21 篇文章 0 订阅

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符合条件

 

字符串为:

ABBABA
123456

 

则利用贪心的思想,先紧着AB匹配,(如果先紧着BA匹配也一样)

 

那么,我们先紧着AB进行匹配(想紧着BA进行匹配也没毛病)

从前缀字串中A、B两个字符的个数

 

前缀子串A的个数B的个数A的个数减去B的个数()删除可以匹配AB或者BA的字符后
A101“A”
AB110“”
ABB12-1“B”
ABBA220“BA”
ABBAB23-1“B”
ABBABA330“”

所以我们利用A、B字符个数的差值(A-B)来表示该前缀字符串中剩余的A或者B(也就是状态转移方程中的状态)

让A当作 1 ,B当作 - 1 

则在ABBABA字符串中,其前缀和表示前缀字串的A、B的差值:

ABBABA
10-10-10

我们通过枚举差值来转移状态

则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;
}

 

 

 

 

写的真烂,自己都看不下去

 

 

 

 

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值