当前位置: 首页 > news >正文

【题解】洛谷 P2470 [SCOI2007] 压缩

P2470 [SCOI2007] 压缩 - 洛谷 (luogu.com.cn)(这真的是紫吗,感觉也就蓝)

看到题,我学的自动机和找规律推公式都按压不住。

看到数据范围泻火了。


题意不要理解错了:

R 重复从上一个 M(如果当前位置左边没有 M,则从串的开始算起)开始的压缩

也就是像:ABCABCABC 这样的串会压缩成 ABCRABC,而 ABCRR 解压后会是 4 个 ABC

更清晰的说,只有 2 的次方个数的回文串才能用很多个连续的 R 压缩。

 其它不符合条件(比如不是 2^x 类型的偶数,和所有奇数)个数的回文串,

只能压缩成很多组不同的 2^x ,中间用 M 阻隔,就像转为二进制数一样。

比如说我有 7 个 ABC,那么最优方案就是先将 4 个 ABC 压缩成 ABCRR,放 M 在后面。

剩下的 3 个 ABC 分成 2 个 和 1 个,最后变成 ABCRRABCRABC。

时间复杂度可以到 O(N^4) 左右,首先排除贪心(时间复杂度太小且不太好想)。

然后想 dp,发现这种区间合并挺像基因序列那道题的,考虑区间 dp

回看题目,发现 M 的设置位置是很重要的,M 会把字符串分成两部分。

这相当于在字符串的中间(放 M 的位置),将字符串前面的字符清零了(从 R 的规则来看)。

因为被 M 分成的两段区域可以自由使用 R,互不干扰,

考虑 O(N^3) 枚举区间的开头结尾和 M 的位置。

而 R 的连续合并操作,前面说过了只能是 2^x 的连续合并,

其他需要 M 阻隔的情况都可以先单独连续合并,然后通过上面枚举 M 位置合并起来。

所以我们这里先考虑最初始不使用 M 的情况,也就是两段单个回文串合并

要求两段串一模一样,这里可以用哈希减小常数,但之前都已经 O(N^3) 了,直接枚举也没事。

举个例子 ABCABCABCABC,就会先把前后两段 ABCABC 合并,

压缩后都是 4(3 [ABC] + 1 [R])个。

然后再把前后两个 ABCR 合并,变成 5(4 [ABCR] + 1 [R])个。

前面已经说过 M 分开的两端可以单独使用 R,而 R 的连续合并操作要求两段都没使用过 M

那么就可以直接将有无使用过 M 加入 dp 状态定义:

(提示:dp[i][j][0/1] 都是假设 i 是整个字符串的开头,即 i 前面放了 M)

 dp[i][j][0] 表示 i 到 j 整一段字符串压缩得到最短的长度,

且没使用过 M 分割(但有可能使用过 R 来连续合并)

 dp[i][j][1] 表示 i 到 j 整一段字符串压缩得到最短的长度,

且使用过 M 分割(也可能根本用不上 M)

注释代码:

#include<bits/stdc++.h>
using namespace std;const int N = 55;
int dp[N][N][2];
/*
(提示:dp[i][j][0/1] 都是假设 i 是整个字符串的开头,即 i 前面放了 M)dp[i][j][0] 表示 i 到 j 整一段字符串压缩得到最短的长度,
且没使用过 M 分割(但有可能使用过 R 来连续合并)。
dp[i][j][1] 表示 i 到 j 整一段字符串压缩得到最短的长度,
且使用过 M 分割(也可能根本用不上 M)。
*/char s[N];bool judge(int l, int r) {int mid = (l + r) >> 1;for (int i = l; i <= mid; i ++) {int j = mid + i - l + 1;if (s[i] != s[j]) {return 0;}}return 1;
}int main () {ios::sync_with_stdio(false);cin.tie(0);cin >> (s + 1);int n = strlen(s + 1);memset(dp, 0x3f, sizeof(dp));    // 求最小值,初始化最大值 for (int L = 1; L <= n; L ++) {   // 先枚举区间长度(区间 dp 常规操作) for (int i = 1; i + L - 1 <= n; i ++) {   // 再枚举区间开端 int j = i + L - 1;    // 区间结尾dp[i][j][0] = dp[i][j][1] = j - i + 1;   // 最坏情况就是根本压缩不了for (int k = i; k < j; k ++) {dp[i][j][1] = min(dp[i][j][1], min(dp[i][k][1], dp[i][k][0]) +1 + min(dp[k + 1][j][1], dp[k + 1][j][0]));// 枚举 M 使用位置,分成的前后两部分可以单独使用 R// 这里不在意前后两部分有没有自己用过 M// 只是保证后面部分的答案合法(k + 1 的前面放了 M) } for (int k = i; k <= j; k ++) {dp[i][j][0] = min(dp[i][j][0], dp[i][k][0] + (j - (k + 1) + 1));// i 到 j 不使用 M,那只有前半部分可以用 R// 后半部分只能不压缩(不能合法使用 R) }if ( (j - i + 1) % 2 == 0 && judge(i, j)) {// 长度是偶数,且前后两段字符串完全相同int mid = (i + j) >> 1; dp[i][j][0] = min(dp[i][j][0], dp[i][mid][0] + 1);// 两段合并(压缩完是连续 R 且不用 M) } }}cout << min(dp[1][n][0], dp[1][n][1]) << "\n";return 0;
} 

http://www.dtcms.com/a/597222.html

相关文章:

  • Java1111 实现一个方法,获取属性值 返回name
  • 存储核心:EXT文件系统
  • 品牌型网站的特点站长工具seo查询5g5g
  • 量子信息中的QASM
  • 阿里巴巴做网站费用网站功能价格表
  • MongoDB 删除数据库
  • android studio 在终端指定 jdk 1.8 打包 release~ MacOS版
  • 【STM32】HDC2080温湿度计驱动
  • Highcharts 在 AMD 与 CommonJS 环境下的安装与配置指南
  • 阿里云编辑建设好的网站济南网络优化厂家
  • 如何给 MacOS 更新到指定版本系统
  • jEasyUI 合并单元格详解
  • AJAX学习 ---- axios体验
  • 电子商务网站开发基本流程建立链接
  • MySQL第五次作业(触发器,存储过程)
  • HORIBA MEXA-324M:双组分汽车尾气测量仪技术说明
  • C++进阶:(八)基于红黑树泛型封装实现 map 与 set 容器
  • 网站制作资质wordpress用户名密码注册
  • Redis(1)
  • 《中医基础理论》- 2.哲学基础之藏象学说-心系统详解
  • 智慧养老如何托起“有温度的银发未来”?
  • 做网站编程在程序网站被别人做镜像
  • win2008 r2内存爆满导致前端程序崩溃
  • 【OpenCV + VS】 OpenCV图像色彩空间转换: 从BGR到HSV和灰度
  • 郓城住房和城乡建设局网站淘宝关键词优化怎么弄
  • 简约 时尚 高端 网站建设表白二维码制作网站
  • Uniapp运行MuMu模拟器
  • uniapp uniim ios配置消息推送
  • 如何监测 Vue + GeoScene 项目中浏览器内存变化并优化性能
  • 使用Rancher快速部署K8S集群