T2980 LR棋盘【Dp+空间/时间优化】
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+空间/时间优化】的更多相关文章
- 树形dp空间优化(dfn)
树形dp空间优化 介绍 有时题目会告诉我们n叉树的最大层数,或者给出一个完全n叉树树,直接做树形dp会爆空间时,就可以用这个优化方法. 多数树形dp都是先dfs到子树,再合并到根上,显然当合并到根上时 ...
- [BZOJ1044][HAOI2008]木棍分割 二分 + 单调队列优化dp + 滚动数组优化dp
Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长 ...
- Codevs 3002 石子归并 3(DP四边形不等式优化)
3002 石子归并 3 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 有n堆石子排成一列,每堆石子有一个重量w[i], 每次 ...
- Linux启动时间优化-内核和用户空间启动优化实践
关键词:initcall.bootgraph.py.bootchartd.pybootchart等. 启动时间的优化,分为两大部分,分别是内核部分和用户空间两大部分. 从内核timestamp 0.0 ...
- dp的斜率优化
对于刷题量我觉得肯定是刷的越多越好(当然这是对时间有很多的人来说. 但是在我看来我的确适合刷题较多的那一类人,应为我对知识的应用能力并不强.这两天学习的内容是dp的斜率优化.当然我是不太会的. 这个博 ...
- CSP 201612-4 压缩编码 【区间DP+四边形不等式优化】
问题描述 试题编号: 201612-4 试题名称: 压缩编码 时间限制: 3.0s 内存限制: 256.0MB 问题描述: 问题描述 给定一段文字,已知单词a1, a2, …, an出现的频率分别t1 ...
- 炮(棋盘DP)
一直以为自己写的就是状态压缩,结果写完才知道是个棋盘dp 首先看一下题目 嗯,象棋 ,还是只有炮的象棋 对于方案数有几种,我第一个考虑是dfs,但是超时稳稳的,所以果断放弃 然后记得以前有过和这个题差 ...
- [poj3017] Cut the Sequence (DP + 单调队列优化 + 平衡树优化)
DP + 单调队列优化 + 平衡树 好题 Description Given an integer sequence { an } of length N, you are to cut the se ...
- Oracle DB 执行表空间时间点恢复
• 列出在执行表空间时间点恢复(TSPITR) 时会发生的操作 • 阐释TSPITR 使用的术语的定义 • 确定适合将TSPITR 用作解决方案的情况 • 确定时间点恢复的正确目标时间 • 确定不能使 ...
随机推荐
- flutter 超出俩行点点点
Text( '${listItem["title"]}', overflow: TextOverflow.ellipsis, maxLines: 2, style: TextSty ...
- 获取从天气预报接口返回回来的json数据
搬迁到了我的新博客中 ==> http://www.suanliutudousi.com/2017/08/26/%E8%8E%B7%E5%8F%96%E4%BB%8E%E5%A4%A9%E6%B ...
- 4.1_springboot2.2任务之异步、定时、邮件任务
1.异步任务 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在Spri ...
- python 17 函数基础(一)
http://www.cnblogs.com/BeginMan/p/3171977.html 一.什么是函数.方法.过程 推荐阅读:http://www.cnblogs.com/snandy/arch ...
- 《转》python 10 集合
自 http://www.cnblogs.com/BeginMan/p/3160565.html 一.目录 1.集合概述 2.关于集合的操作符.关系符号 3.集合的一系列操作(添加.更新.访问.删除) ...
- HDU 5052 /// 树链剖分+线段树区间合并
题目大意: 给定n (表示树有n个结点) 接下来n行给定n个点的点权(在这个点上买鸡或者卖鸡的价钱就是点权) 接下来n-1行每行给定 x y 表示x结点和y结点之间有一条边 给定q (表示有q个询问) ...
- selenium基础-跳过验证码
selenium基础-跳过验证码 一.方法 设置万能验证码或者屏蔽验证码(最常用的方法) 使用验证码识别工具识别验证码 通过selenium操作cookies 直接使用配置文件的webdriver 二 ...
- [转]Visual Studio 各版本下载
原文地址:[置顶] Visual Studio 各版本下载 文件名称 文件大小 百度网盘下载 微软官方下载 Visual Studio 2015 Enterprise - 企业版 - 简体中文 3.8 ...
- matlab中乘法和点乘以及除法和点除的联系是什么?
一,*和.*的联系和区别. 1,在进行数值运行和数值乘矩阵,这两种没有区别,例如:a*b=a.*b; a*B=a.*B; B*a=B.*a (其中小写字母表示数值,大写字母表示矩阵,下同). 2,在处 ...
- thinkphp 批量配置
C配置方法支持批量配置,例如: $config = array('WEB_SITE_TITLE'=>'ThinkPHP','WEB_SITE_DESCRIPTION'=>'开源PHP框架' ...