CF909C. Python Indentation 【dp + 思维】

链接

https://codeforces.com/problemset/problem/909/C

描述

这个题是吐槽python用tab做域界定符的。。
给你一个n行的python程序,每行要么是 for 语句, 要么是普通语句
其中for语句不允许空循环体
问你有多少种方式加tab界定域,使代码执行路径不同
(保证给定程序有效)

分析

首先我们可以看出一个普通语句前方紧挨着的一堆for 与 这个普通语句 是一个整体。
也就是说 “ffffffffffffs” 这个结构,在每个合法方案中的表达形式都是一致的,所以我们先将输入做一个转化,转化为 记录每个s前有多少个f的形式。
例:

fffs
s
ffs

转化为

3
0
2

然后再来看看每一个fs整体连接到已存在的一堆代码时都干了什么

  1. 选择一系列 已存在的一堆代码 的排列方式,它们的共性是最后的语句的缩进层数是deep层
  2. 向前缩进i个单位(0 <= i <= deep)
  3. __把整体中的第一个f连到它上面(缩进层数为deep-i),产生新的连接方式
  4. __选择新的i,回到2,直到遍历0到deep所有的i
  5. 选择新的系列(新的deep),回到1,直到遍历所有的系列

知道这些我们就可以开始dp了,设dp[i][deep] = 添加前i个fs整体后 ,产生的新结构中最后一的语句的缩进层数是deep层的种类数
那么初始状态就只有dp[0][0] = 1,其余都是0。

接着来看如何转移
从上面那组数字开始看起,我们的dp数组起始如下

deep012345678910
dp[0][deep]10000000000

表示一个空行只有结尾缩进为0一种答案
那么我们接上第一个长度为3的fs结构时,我们要进行以下操作

  1. 选择dp[0]层中非0元素dp[0][0] = 1
  2. 缩进0到0个单位
  3. 更新答案
    dp[1][0+3] += dp[0][0] = 1
deep012345678910
dp[1][deep]00010000000

接第二个长度为0的结构时,操作如下

  1. 选择dp[1]层中非0元素dp[1][3] = 1
  2. 缩进0到3个单位
  3. 更新答案
    dp[2][0+0] += dp[1][3] = 1
    dp[2][1+0] += dp[1][3] = 1
    dp[2][2+0] += dp[1][3] = 1
    dp[2][3+0] += dp[1][3] = 1
deep012345678910
dp[2][deep]11110000000

接第三个长度为2的结构时,操作如下

  1. 选择dp[2]层中非0元素dp[2][3] = 1
  2. 缩进0到3个单位
  3. 更新答案
    dp[3][0+2] += dp[2][3] = 1
    dp[3][1+2] += dp[2][3] = 1
    dp[3][2+2] += dp[2][3] = 1
    dp[3][3+2] += dp[2][3] = 1
  4. 选择dp[2]层中非0元素dp[2][2] = 1
  5. 缩进0到2个单位
  6. 更新答案
    dp[3][0+2] += dp[2][2] = 1
    dp[3][1+2] += dp[2][2] = 1
    dp[3][2+2] += dp[2][2] = 1
  7. 选择dp[2]层中非0元素dp[2][1] = 1
  8. 缩进0到1个单位
  9. 更新答案
    dp[3][0+2] += dp[2][1] = 1
    dp[3][1+2] += dp[2][1] = 1
  10. 选择dp[2]层中非0元素dp[2][0] = 1
  11. 缩进0到0个单位
  12. 更新答案
    dp[3][0+2] += dp[2][0] = 1

综上,我们发现了两条有用的信息

1. dp[3][0+2] += dp[2][3] +  dp[2][2] +  dp[2][1] +  dp[2][0] ; 
   dp[3][1+2] += dp[2][3] +  dp[2][2] +  dp[2][1]  ; 
   dp[3][2+2] += dp[2][3] +  dp[2][2] ; 
   dp[3][3+2] += dp[2][3] ; 
   每个加的是个后缀和
2. +2偏移是接的长度,加新的、长度为l的fs结构时,会使结果整个向后推l位,前面填0

所以咱们操作的时候把它分成两步就很容易了

  1. 倒着加 dp[i] += dp[i+1]
  2. 后移l位,前面填0

显然也很容易直接把第一维压掉

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

const ll DSY = 1000000000+7;
const int MAXN = 5000+5;

ll dp[MAXN];
int len;
bool iss[MAXN];

int main(void){

    scanf("%d",&len);
    for(int i=0;i<len;i++){
        char str[2];
        scanf("%s",str);
        iss[i] = (str[0] == 's');
    }

    vector<int> blocks;
    int fcnt = 0;
    for(int i=0;i<len;i++){
        if(iss[i]){
            blocks.push_back(fcnt);
            fcnt = 0;
        }else{
            fcnt++;
        }
    }

    dp[0] = 1;
    for(auto elm:blocks){
        for(int i=5000;i>=0;i--){
            dp[i] += dp[i+1];
            dp[i] %= DSY;
        }
        for(int i=5000;i>=0;i--){
            dp[i] = ( (i-elm >= 0 ) ? (dp[i-elm]) : (0) );
        }
    }

    ll ans = 0;
    for(int i=5000;i>=0;i--){
        ans += dp[i];
    }
    ans  %= DSY;
    cout << ans;

    return 0;
}

才疏学浅,如有错误,欢迎指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值