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. LUOGU P3723 [AH2017/HNOI2017]礼物 (fft)

    传送门 解题思路 首先我们设变化量为\(r\),那么最终的答案就可以写成 : \[ ans=min(\sum\limits_{i=1}^n(a_i-b_i+r)^2) \] \[ ans=min(\s ...

  2. Codeforces786B

    传送门 n个节点且固定起点最短路,三种加边方法 1.u->v, 边权为w:2. u->[l, r], 边权为w:3. [l, r]->u, 边权为w AC_Code #include ...

  3. spring其他配置 (3)

    目录 一.自动装配 Autowired 二.bean的作用于singleton,prototype 三.引入外部资源properties文件 四.SpEL表达式 (可以为属性进行动态的赋值) 五.通过 ...

  4. Python中将dict转换为kwargs

    Python中将dict转换为kwargs 我们都知道kwargs是变长kv参数,能否将dict转换成kwargs. 在python调用函数的时候func(**{'type'='event'}),可以 ...

  5. Matlab求三重积分

    Matlab求三重积分 求 \(\int_0^1 \int_0^1 \int_0^1 sin(\pi x_1 x_2 x_3) dx_1 dx_2 dx_3\) 代码是: triplequad(@(x ...

  6. 数据结构C++版-图

    一.概念及分类 二.图的存储结构 1.邻接矩阵 顶点: 弧: 边: 表达式语句: 2.邻接表 逆邻接表: 3.十字链表 4.邻接多重表 三.图的权值概念及遍历 权值: 图的遍历: 1.深度优先搜索 2 ...

  7. BackgroundWorker study

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  8. 基于三角形与位置指纹识别算法的WiFi定位比较

    文章来着:https://wenku.baidu.com/view/55d1f4146edb6f1aff001fec.html

  9. js面向对象编程:this究竟代表什么?第二篇

         总觉得自己弄明确了js中this的含义.this总是指向调用方法的对象,作为方法调用,那么this就是指实例化的对象.但前几天自己写脚本却遇到了一个非常奇怪的问题.    代码例如以下:   ...

  10. HTML5的特殊标签与IE浏览器的兼容

    注释标签 ruby: 行级元素 横排显示 试图写多个汉字和注释,需要多个ruby. 直接上代码: - css样式: 页面效果: 重点标记 mark: 以灰常黄的黄色来重点标记 页面代码: 类似于进度条 ...