Online Judge:未知

Label:Dp+滚动+前缀和优化

题目描述

有一个长度为1*n的棋盘,有一些棋子在上面,标记为L和R。

每次操作可以把标记为L的棋子,向左移动一格,把标记为R的棋子,向右移动一格,前提条件是目标格子为空。结果任意多次操作之后,棋盘会有多少种不同的状态?

输入

第一行一个字符串,包含'L','R','.'。

输出

输出所有不同的棋盘状态,对1e9+7 求余。

样例

Input

R..

R.L

...R..L..L

R..L......LLR.RRL..RR.....RR.L.RR.....L..R....RR.........R..RLL..RL...RLL.LLRR......RLRRR...R......R

Output

3

3

22

709788833

Hint

样例2的最终状态为 "R.L",".RL","RL."

对于10%的数据,棋子数为1

对于30%的数据,棋子数不超过2,棋盘长度不超过10

对于40%的数据,棋子数不超过5,棋盘长度不超过10

对于60%的数据,棋子数不超过50,棋盘长度不超过500

对于90%的数据,棋子数不超过100,棋盘长度不超过10000

对于100%的数据,棋子数不超过2000,棋盘长度不超过20000。


题解

40pts

观察到一个性质,棋子与棋子之间的相对位置是不会变的。考虑枚举每个棋子的位置,最后检验一遍。运行次数大约为\(C(10,5)*5\)。

大致代码如下

namespace p40{
int a[12],ans=0;
inline bool check(){
for(int i=1;i<=cnt;++i){
if(s[pos[i]]=='L'&&a[i]>pos[i])return 0;
if(s[pos[i]]=='R'&&a[i]<pos[i])return 0;
}
return 1;
}
void dfs(int x,int lst){
if(x==cnt+1){if(check())ans++;return;}
for(int i=lst+1;i<=n;++i){a[x]=i;dfs(x+1,i);}
}
void solve(){dfs(1,0);printf("%d\n",ans);}
}
int main(){
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;++i){
if(s[i]=='.')continue;
pos[++cnt]=i;
}
}

100pts

L只能往左边走,R只能往右边走。由此我们可以循环一下计算出每个L,R能到达的最左最右位置(\(li[i],ri[i]\) \(i∈[1,cnt]\))。

代码如下, 应该可以写得更简洁一点。

void pre(){
int now=n+1,i,j;
for(int i=cnt;i>=1;i--)if(s[pos[i]]!='.'){
ri[i]=now-1;
if(s[pos[i]]=='L')now=pos[i];
}
now=0;
for(int i=1;i<=cnt;++i)if(s[pos[i]]!='.'){
li[i]=now+1;
if(s[pos[i]]=='R')now=pos[i];
}
for(int i=1;i<=cnt;++i){
if(s[pos[i]]=='L')ri[i]=pos[i];
if(s[pos[i]]=='R')li[i]=pos[i];
}
}

这样问题变成了:

给定cnt个空,第i个空可以填的数字范围为\(li[i]\)~\(ri[i]\),问有多少种方案,使得序列严格递增。

之前类似的问题有遇到过:T2988 删除数字【状压Dp+前缀和优化】

下面重新分析一下这类问题。

假如题目令填完的序列单调递增(严格),元素个数为\(cnt\),元素取值范围为\(n\)

定义状态\(dp[i][j]\)表示,已经填完了前\(i\)个,且第\(i\)个空填的数字大小为\(j\)时,前面的方案数。(定义成后面,逆推也可以)

  • 最朴素的转移就是枚举i,枚举i取的数字,枚举i-1取的数字,时间复杂度为\(O(cnt\cdot n^2)\),空间复杂度为\(O(cnt\cdot n)\)。

  • 时间优化:利用前缀和/后缀和优化,免去枚举i-1取的数这一维,时间复杂度为\(O(cnt\cdot n)\),空间复杂度仍然为\(O(cnt\cdot n)\)。

  • 空间优化:比如说对于这道题,如果数组开成\(dp[cnt][n]\),第10个点会MLE。既然只用到前一个位置的状态,容易想到滚动掉第一维变成\(dp[2][n]\)(tip1),也可以改变转移顺序,这样只用到一维\(dp[cnt]\),空间上大大优化(tip2)。

综上时间复杂度最优为\(O(cnt\cdot n)\),dp数组大小只用开到\(cnt\)。

本题代码如下,预处理函数\(pre\)已经在前面给出:

代码1:

#include<bits/stdc++.h>
#define mod 1000000007
const int N=20005,maxn=2005;
char s[N];
int n,li[maxn],ri[maxn],cnt,pos[maxn],dp[maxn];
void pre(){}
int main(){
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;++i)if(s[i]!='.')pos[++cnt]=i;
pre();
dp[0]=1;
for(int i=1;i<=n;i++)for(int j=cnt;j>=1;j--){
if(li[j]<=i&&i<=ri[j])dp[j]=(dp[j]+dp[j-1])%mod;
}
printf("%d",dp[cnt]);
}

奉上赛时代码,用了滚动优化空间,而且是逆推,看起来比较乱。

代码2

#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
typedef long long ll;
bool nc1;
const int maxn=20005;
char s[maxn];
int n,cnt,pos[2010];
ll dp[2][maxn],sum[maxn];
namespace p40{
int a[12],ans=0;
inline bool check(){
for(int i=1;i<=cnt;++i){
if(s[pos[i]]=='L'&&a[i]>pos[i])return 0;
if(s[pos[i]]=='R'&&a[i]<pos[i])return 0;
}
return 1;
}
void dfs(int x,int lst){
if(x==cnt+1){if(check())ans++;return;}
for(int i=lst+1;i<=n;++i){a[x]=i;dfs(x+1,i);}
}
void solve(){dfs(1,0);printf("%d\n",ans);}
}
inline void Do(ll &x,ll y){
x+=y;
if(x>=mod)x-=mod;
}
int li[2010],ri[2010];
bool nc2;
int main(){
// cout<<(&nc2-&nc1)/1024/1024<<endl;
// freopen("board.in","r",stdin);
// freopen("board.out","w",stdout);
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;++i){
if(s[i]=='.')continue;
pos[++cnt]=i;
} if(n<=10){
p40::solve();
return 0;
}
register int now=n+1,i,j;
for(i=cnt;i>=1;i--)if(s[pos[i]]!='.'){
ri[i]=now-1;
if(s[pos[i]]=='L')now=pos[i];
}
now=0;
for(i=1;i<=cnt;++i)if(s[pos[i]]!='.'){
li[i]=now+1;
if(s[pos[i]]=='R')now=pos[i];
}
for(i=1;i<=cnt;++i){
if(s[pos[i]]=='L')ri[i]=pos[i];
if(s[pos[i]]=='R')li[i]=pos[i];
} memset(dp,0,sizeof(dp));
int g=0;
for(i=li[cnt];i<=ri[cnt];++i)dp[g][i]=1;
for(i=ri[cnt];i>=1;i--)sum[i]=(sum[i+1]+dp[g][i])%mod; for(i=cnt-1;i>=1;i--){
g^=1;memset(dp[g],0,sizeof(dp[g]));
for(j=li[i];j<=ri[i];++j)Do(dp[g][j],sum[j+1]);
memset(sum,0,sizeof(sum));
for(j=ri[i];j>=1;j--){
sum[j]=sum[j+1];
Do(sum[j],dp[g][j]);
}
} printf("%lld\n",(sum[li[1]]+mod)%mod);
}

T2980 LR棋盘【Dp+空间/时间优化】的更多相关文章

  1. 树形dp空间优化(dfn)

    树形dp空间优化 介绍 有时题目会告诉我们n叉树的最大层数,或者给出一个完全n叉树树,直接做树形dp会爆空间时,就可以用这个优化方法. 多数树形dp都是先dfs到子树,再合并到根上,显然当合并到根上时 ...

  2. [BZOJ1044][HAOI2008]木棍分割 二分 + 单调队列优化dp + 滚动数组优化dp

    Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长 ...

  3. Codevs 3002 石子归并 3(DP四边形不等式优化)

    3002 石子归并 3 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 有n堆石子排成一列,每堆石子有一个重量w[i], 每次 ...

  4. Linux启动时间优化-内核和用户空间启动优化实践

    关键词:initcall.bootgraph.py.bootchartd.pybootchart等. 启动时间的优化,分为两大部分,分别是内核部分和用户空间两大部分. 从内核timestamp 0.0 ...

  5. dp的斜率优化

    对于刷题量我觉得肯定是刷的越多越好(当然这是对时间有很多的人来说. 但是在我看来我的确适合刷题较多的那一类人,应为我对知识的应用能力并不强.这两天学习的内容是dp的斜率优化.当然我是不太会的. 这个博 ...

  6. CSP 201612-4 压缩编码 【区间DP+四边形不等式优化】

    问题描述 试题编号: 201612-4 试题名称: 压缩编码 时间限制: 3.0s 内存限制: 256.0MB 问题描述: 问题描述 给定一段文字,已知单词a1, a2, …, an出现的频率分别t1 ...

  7. 炮(棋盘DP)

    一直以为自己写的就是状态压缩,结果写完才知道是个棋盘dp 首先看一下题目 嗯,象棋 ,还是只有炮的象棋 对于方案数有几种,我第一个考虑是dfs,但是超时稳稳的,所以果断放弃 然后记得以前有过和这个题差 ...

  8. [poj3017] Cut the Sequence (DP + 单调队列优化 + 平衡树优化)

    DP + 单调队列优化 + 平衡树 好题 Description Given an integer sequence { an } of length N, you are to cut the se ...

  9. Oracle DB 执行表空间时间点恢复

    • 列出在执行表空间时间点恢复(TSPITR) 时会发生的操作 • 阐释TSPITR 使用的术语的定义 • 确定适合将TSPITR 用作解决方案的情况 • 确定时间点恢复的正确目标时间 • 确定不能使 ...

随机推荐

  1. NOIp2018集训test-9-17(pm)

    T1记忆(memory) 我大概是只记忆只有七秒的金鱼吧.看了下以前的代码发现真的很简单,但是考场上只打了个暴力,虽然骗了88pt.就是枚举选的是哪个串,然后vis[i]表示选了i这些位能不能猜出它, ...

  2. NX二次开发-获取面的法向向量UF_MODL_ask_face_data

    NX9+VS2012 #include <uf.h> #include <uf_modl.h> #include <uf_obj.h> #include <u ...

  3. NX二次开发-UFUN输入特征TAG,获取特征所有表达式TAG和个数UF_MODL_ask_exps_of_feature

    NX9+VS2012 #include <uf.h> #include <uf_modl.h> UF_initialize(); //创建块 UF_FEATURE_SIGN S ...

  4. [JZOJ 5600] Arg

    题意:求最少LIS覆盖... 思路: 计算\(LIS\)时我们一般用\(dp\)表示到当先位置时以当前位置结尾的\(LIS\)最长长度. 那么这个数组保证单调不降,我们考虑二进制表示. 然后就是转移了 ...

  5. Openstack Nova 源码分析 — 使用 VCDriver 创建 VMware Instance

    目录 目录 前言 流程图 nova-compute vCenter 前言 在上一篇Openstack Nova 源码分析 - Create instances (nova-conductor阶段)中, ...

  6. 拾遗:{rpm、yum及源码方式管理软件包}

    一.yum配置文件位置 /etc/yum.conf /etc/yum.repos.d/*.repo 二.yum常用命令 install pkgs reinstall pkgs update pkgs ...

  7. Cesium资料大全

    前言 Cesium是一个用于显示三维地球和地图的开源js库.它可以用来显示海量三维模型数据.影像数据.地形高程数据.矢量数据等等.三维模型格式支持gltf.三维瓦片模型格式支持3d tiles.矢量数 ...

  8. 编译Solr4.72 源码没有成功

    最近需要用到solr,查询Hbase里面的数据,编译Solr的时候遇到了点问题: 下了solr的源码后需要用ant自己编译: 源码下载地址:https://svn.apache.org/repos/a ...

  9. VO和DO的区别

    阿里巴巴Java开发手册中的DO.DTO.BO.AO.VO.POJO定义 分层领域模型规约: DO( Data Object):与数据库表结构一一对应,通过DAO层向上传输数据源对象. DTO( Da ...

  10. 解决在python中进行CGI编程时无法响应的问题

    问题:我期望的效果是,后端解析脚本后,将结果返回给我,而不是将代码返回给我或者是让我下载文件. 参考地址:https://blog.csdn.net/C_chuxin/article/details/ ...