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. flutter 超出俩行点点点

    Text( '${listItem["title"]}', overflow: TextOverflow.ellipsis, maxLines: 2, style: TextSty ...

  2. 获取从天气预报接口返回回来的json数据

    搬迁到了我的新博客中 ==> http://www.suanliutudousi.com/2017/08/26/%E8%8E%B7%E5%8F%96%E4%BB%8E%E5%A4%A9%E6%B ...

  3. 4.1_springboot2.2任务之异步、定时、邮件任务

    1.异步任务 ​ 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在Spri ...

  4. python 17 函数基础(一)

    http://www.cnblogs.com/BeginMan/p/3171977.html 一.什么是函数.方法.过程 推荐阅读:http://www.cnblogs.com/snandy/arch ...

  5. 《转》python 10 集合

    自 http://www.cnblogs.com/BeginMan/p/3160565.html 一.目录 1.集合概述 2.关于集合的操作符.关系符号 3.集合的一系列操作(添加.更新.访问.删除) ...

  6. HDU 5052 /// 树链剖分+线段树区间合并

    题目大意: 给定n (表示树有n个结点) 接下来n行给定n个点的点权(在这个点上买鸡或者卖鸡的价钱就是点权) 接下来n-1行每行给定 x y 表示x结点和y结点之间有一条边 给定q (表示有q个询问) ...

  7. selenium基础-跳过验证码

    selenium基础-跳过验证码 一.方法 设置万能验证码或者屏蔽验证码(最常用的方法) 使用验证码识别工具识别验证码 通过selenium操作cookies 直接使用配置文件的webdriver 二 ...

  8. [转]Visual Studio 各版本下载

    原文地址:[置顶] Visual Studio 各版本下载 文件名称 文件大小 百度网盘下载 微软官方下载 Visual Studio 2015 Enterprise - 企业版 - 简体中文 3.8 ...

  9. matlab中乘法和点乘以及除法和点除的联系是什么?

    一,*和.*的联系和区别. 1,在进行数值运行和数值乘矩阵,这两种没有区别,例如:a*b=a.*b; a*B=a.*B; B*a=B.*a (其中小写字母表示数值,大写字母表示矩阵,下同). 2,在处 ...

  10. thinkphp 批量配置

    C配置方法支持批量配置,例如: $config = array('WEB_SITE_TITLE'=>'ThinkPHP','WEB_SITE_DESCRIPTION'=>'开源PHP框架' ...