P2470 压缩 TJ
前言
正解:区间/线性 dp(本篇题解介绍线性做法)
人生第一道紫题!
也是今天考试看自闭了就没做的 T4,结果没想到是紫,虽然是一道水紫呢……
考试的 T5 是跳房子,蓝题 qwq。要不是前三题比较简单 + 骗分好骗(指靠直接输出字符串长度骗了十分)就真的自闭了。
不管怎么样反正挺难的。
题解
我们观察到一个字符串压缩的程度和 “M”,“R”的个数是有关的,尤其是开始一段压缩区间的“M”,非常的重要,因为它的位置决定了压缩串的长度,也直接决定了最后字符串的长度。非常好的条件,可以利用。
所以,定义 \(dp\) 数组状态为:
- \(dp_{i,j}\) 代表前 \(i\) 个字符,上一个“M”的位置在第 \(j\) 个字符前所能达到的最短长度。
\(dp\) 数组的初始值:
当只有一个字符时,“M”只有可能在第一个字符前,而且长度为 \(1\)。所以,\(dp_{1,1}=1\)。
于是我们可以分析出以下三种情况。
直接在前面 \(i-1\) 个字符的情况下,加上一个字符。
添加压缩的字符串。
新建一个压缩字符串部分的开头。
part1
如果只是直接加一个,这样 \(j\) 的值是不会变的,因为我们没有考虑“M”和“R”的情况。这样得到的答案就是 \(dp_{i-1,j}+1\)。
part2
我们知道,当且仅当某两个字符子串完全相同时,我们才能对这两个字符串进行一次压缩。
至于这个压缩的地方加在哪里呢?因为最近的“M”的位置已经确定,所以这个“M”(坐标为 \(j-1\) 和 \(j\))之前(并不包括第 \(j\) 个字符)的所有字符串情况已经确定,无法改动(因为 dp 不能有后效性)。
所以我们确定被复制了一遍的区域在 \([j,i]\),从中间切开这份字符串,看看两边是不是相等的,如果是就可以考虑这种情况。注意这段区域的的长度为奇数是是一定不可能的。
判断字符串是否相等的函数很简单,就是这样:
bool check(int l,int mid,int r){
if((r-l+1)&1) return 0; //如果长度是奇数直接不可能
for(int i=0;i<=mid-l;i++){
if(a[l+i]!=a[mid+1+i]) return 0; //出现不一样的字符
}
return 1; //全部都一样
}
然后考虑如何状态转移,很明显,这样的长度就是 \(j\) 之前的那些字符的最短长度 + 压缩后 \([j,i]\) 段的长度 + \(1\)(那个“R”字符)。转化为情况二的动态转移方程即为:
\]
其中 \(\lfloor \dfrac{i+j}{2} \rfloor\) 表示的是被切成两段的那一部分的前面那个字符串的最后一个字符串的下标(好绕啊)。
part3
因为要新建一个“M”,意思就是再开一个可重复的字符串并将新开的“M”的下标作为 \(dp\) 数组的第二个值。因为我们枚举到 \(i\) 了,所以最方便的就是把这个“M”加在 \(i-1\) 和 \(i\) 之间,这样每一次循环都能考虑到一层,就全部考虑到了。
那转移方程是什么呢?我们继续观察,现在我们考虑的范围就已经缩小到了 \(1\) 到现在添加的“M”(坐标为 \(i-1\) 和 \(i\) 之间)中间的这一段。很明显 \(i\) 已经在这个“M”后面了,此时我们不需要考虑,所以只需考虑 \([1,i-1]\) 压缩后的最小长度。
那第二个下标呢?因为考虑前面的时候并没有考虑添加的“M”(这是两个情况),所以依然是枚举 \(j\)。
对了,这个值还要 \(+2\),一个是添加的“M”字符,另一个是下标为 \(i\) 的字符。(是的!这个也要算进去!因为此字符是在添加的“M”后面的,前面的式子并没有考虑到)。
所以,情况三的转移方程是:
\]
将三个合并一下,不过记得,要保存前面情况的最优解。除了上述的特殊初始值外,因为求的是最小值,所以 \(dp\) 初始值设为 \(\text{INF}\)(极大值)。
Code
#include<cstdio>
#include<cstring>
#include<climits> // INT_MAX的头文件
#define min(a,b) (a)<(b)?(a):(b)
int n,dp[505][505];
char a[505];
bool check(int l,int mid,int r){
if((r-l+1)&1) return 0;
for(int i=0;i<=mid-l;i++){
if(a[l+i]!=a[mid+1+i]) return 0;
}
return 1;
}
int main(){
//freopen("compress.in","r",stdin);
//freopen("compress.out","w",stdout);
memset(dp,127,sizeof(dp));
dp[1][1]=1;
scanf("%s",a+1);
n=strlen(a+1);
for(int i=2;i<=n;i++){
for(int j=1;j<i;j++){
dp[i][j]=dp[i-1][j]+1;
if(check(j,(j+i-1)/2,i)) dp[i][j]=min(dp[i][j],dp[(i+j-1)/2][j]+1);
}
for(int j=1;j<i;j++) dp[i][i]=min(dp[i][i],dp[i-1][j]+2);
}
int ans=INT_MAX;
for(int i=1;i<=n;i++) ans=min(ans,dp[n][i]);
printf("%d",ans);
return 0;
}
写在最后
考试竟然有个 dalao (zdj 大佬!)写出来了啊……%%%。
虽然听着不难但是思路很难想呢 qwq。
最后请随手点个赞吧,毕竟赠人玫瑰手有余香嘛。
P2470 压缩 TJ的更多相关文章
- luogu P2470 [SCOI2007]压缩
传送门 dalao们怎么状态都设的两维以上啊?qwq 完全可以一维状态的说 设\(f[i]\)为前缀i的答案,转移就枚举从前面哪里转移过来\(f[i]=min(f[j-1]+w(j,i))(j\in ...
- 【洛谷P2470】[SCOI2007]压缩
压缩 #include<iostream> #include<cstring> #include<cstdio> using namespace std; #def ...
- 洛谷P2470||bzoj1068 [SCOI2007]压缩
bzoj1068 洛谷P2470 区间dp入门题?只要注意到每个M“管辖”的区间互不相交即可 错误记录:有点小坑,比如aaaacaaaac最优解为aRRcR(意会坑在哪里),踩了一次 #include ...
- P2470 [SCOI2007]压缩
传送门 区间dp,记\(dp(l,r,t)\)表示区间\((l,r)\),\(t\)表示这个区间中能不能放\(M\).如果可以,枚举中间哪里放\(M\)来压缩.也可以不压缩,后面直接跟上去.如果左右重 ...
- 洛谷P2470 [SCOI2007]压缩(区间dp)
题意 题目链接 Sol 神仙题Orz 考虑区间dp,如果我们只设\(f[l][r]\)表示\(s_{lr}\)被压缩的最小长度,而不去关心内部\(M\)分布的话,可能在转移的时候转移出非法状态 因此考 ...
- HDU2553N皇后问题(状态压缩)
这道题其实最简单的方法就是打表,直接DFS会超时,那就先运行一遍,找出1~10的值,打表即可,这里提供DFS和打表的数据 DFS:(白书上的)TLE #include <stdio.h> ...
- 【算法】【网络流24题】巨坑待填(成功TJ,有时间再填)
------------------------------------------------------------------------------------ 17/24 --------- ...
- 关于seaJs合并压缩(gulp-seajs-combine )路径与文件ID匹配问题。
前段时间和有大家介绍过用 gulp-seajs-combine 来打包seaJs文件.大家会发现合并seaJs一个很奇怪的现象,那就是它的 ID和路径匹配原则.使得有些文件已经合并过去了,但还是会提示 ...
- ASP.NET Core如何使用压缩中间件提高Web应用程序性能
前言 压缩可以大大的降低我们Web服务器的响应速度,压缩从而提高我们网页的加载速度,以及节省一定的带宽. 何时使用相应压缩中间件 在IIS,Apache,Nginx中使用基于服务端的响应压缩技术.中间 ...
随机推荐
- ubuntu开机卡在/dev/sda* clean
问题描述: ①Ubuntu通过再生龙从一台笔记本还原到另外一台笔记本(硬盘到硬盘),开机后卡在自检界面: ②备份前的笔记本为17年发布的笔记本,还原后的笔记本为2020款发布的笔记本 从网上搜了一大篇 ...
- 41、解决du与df统计不一致的解决方法
41.1.案例说明: 通过df -hT和du -sh /.du -h --max-depth=1 /命令 发现磁盘的使用不量不一致,使用'df -hT'命令查看磁盘的使用量要 比使用'du -sh / ...
- 使用 python 把一个文件生成 C 语言中的数组并保存到头文件中
(一)要做什么 之前有这么一个需求,是要把一个二进制文件里面的数据,转换成 C 代码里面的数组,可以看之前的一篇文章: NUC980 运行 RT-Thread 驱动 SPI 接口 OLED 播放 ba ...
- ansible 任务执行
ansible 任务执行模式 Ansible 系统由控制主机对被管节点的操作方式可分为两类,即adhoc和playbook: ad-hoc模式(点对点模式)• ad-hoc模式(点对点模式) 使用单个 ...
- 导入项目发现没得右边没得maven
使用ctrl + shift+A点Add Maven Project 就行了 参考:https://www.cnblogs.com/Juff-code/p/13390356.html
- Spring:Spring事务手动回滚方式
方法1: 在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句 ...
- Mybatis学习(8)动态sql语句
Mybatis 的动态sql语句是基于OGNL表达式的.可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类: 1. if 语句 (简单的条件判断) 2. ...
- [小技巧] Windows7 半角全角快捷键 修改方法
From : http://blog.sina.com.cn/s/blog_87ab67b10100x3ww.html 转载说明:在浏览器下我们可以使用空格下翻一页,Shift + 空格上翻一页. 但 ...
- bugKu管理员系统
先F12看看,有啥发现的,发现一段注释... 感谢那个群友分享了怎么辨别base64编码,通常是A-Z,a-z,0-9,+,/,=.最后通常有0个到2个等号,我也成功用在线解码器,确实是base64编 ...
- PYTHON 读取ADB记录文件输入ACTIVITY
import re lb=[] with open("daaa.txt",encoding="utf8") as f: data = f.readlines() ...