为了能到远方,脚下的每一步都不能少.|

lucky_cloud

园龄:1年4个月粉丝:1关注:3

区间DP入门

石子合并

别人讲过太多了,蒟蒻就不说了。

Polygon

这题跟石子合并类似,只是多输出了个先清除哪条边可以使得值最大。

因为我们不确定先删那一条,我们就再复制一遍添到输入的结尾,就变成了 \(2 \times N - 1\)

我们思考最大值是由哪些贡献的。

  1. 最大值与最大值运算。
  2. 最小值乘上最小值(因为当负数乘上负数时,会变为正数)。

那么最小值。

  1. 最大值乘上最小值(当最大值为正数时,反而会更小)。
  2. 最小值加上最

我们设 \(f1_{i,j}\) 为以 \(i\) 为左端点 \(j\) 为右端点这个区间内的最大值, \(f2_{i,j}\) 为最小值。

定义一个函数:

int deal(int x, int y, int op) {
if (op == 't') return x + y;
return x * y;
}

动态转移方程是

\[f1(i,j) = \max(deal(f1(k+1,j), f1(i,k),op_{k+1}),deal(f2(k + 1,j), f2(i,k),op_{k + 1})) \]

\[f2_(i,j) = \min(deal(f2(k+1,j), f2(i,k),op_{k+1}),deal(f1_(k + 1,j), f2_(i,k),op_{k + 1})) \]

最后我们去寻找以 \(i\) 为起点的,长度为 \(n\) 的最大值,如果等于最后答案,那么便输出,因为这样一定能删去第 \(i\) 条边,得到最大值。

串折叠 Folding&秘密文件&[SCOI2003] 字符串折叠

三倍经验,快乐。

就拿第一个讲。

我们设 \(dp[i][j]\) 表示字符串中 \(s[i\sim j]\) 折叠后的最小长度。

它可以有两种方式转移而来:

  1. 由两段子串折叠后的最小长度加起来。
  2. 以一段子串为循环元,然后出折叠后的长度。

我们通过:

for (int i = l; i + k <= r; i++) {
if (s[i] != s[i + k]) return 0;
}
return 1;

去找到循环元。

于是:

\[dp[i][j] = min(dp[i][k] + dp[k+1][j], dp[i][k] + 2 + m[(j - i + 1) \div (k - i + 1]) \]

其中,\(m\) 数组时每个数的位数。因为 \(s[i\sim k]\) 为循环元,那么就会有 \((j - i + 1) \div (k - i + 1)\) 个循环元,再加上括号的个数,便为折叠后的个数。

我们还要求出字符串是什么,那么在转移时加上即可。

上代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 200;
int n, f[N][N], m[N];
string s, ans[N][N];
bool check(int l, int r, int k) {
for (int i = l; i + k <= r; i++) {
if (s[i] != s[i + k]) return 0;
}
return 1;
}
string shu(int x) {//求出数字的字符串
string res = "";
while (x) {
res = char(x % 10 + '0') + res;
x /= 10;
}
return res;
}
int main() {
for (int i = 1; i <= 9; i++) m[i] = 1;
for (int i = 10; i < 100; i++) m[i] = 2;
for (int i = 100; i <= 199; i++) m[i] = 3;
while (cin >> s) {
n = s.size();
s = " " + s;
for (int l = 0; l < n; l++) {//区间DP
for (int i = 1; i + l <= n; i++) {
int j = l + i;
f[i][j] = l + 1;
ans[i][j] = s.substr(i, l + 1);//初始化
for (int k = i; k < j; k++) {
if (f[i][j] > f[i][k] + f[k + 1][j]) {
f[i][j] = f[i][k] + f[k + 1][j];
ans[i][j] = ans[i][k] + ans[k + 1][j];//求出字符串
}
else if (f[i][j] == f[i][k] + f[k + 1][j]) {
ans[i][j] = max(ans[i][j], ans[i][k] + ans[k + 1][j]);//如果个数相同,找到字典序最大的一项。
}
}
for (int k = i; k < j; k++) {
int len = k - i + 1;
if ((l + 1) % len == 0 && check(i, j, len)) {
string sub = shu((l + 1) / len) + '(' + ans[i][k] + ')';//以 s[i~k] 为循环元的字符串
if (f[i][j] > f[i][k] + 2 + m[(l + 1) / len]) {
f[i][j] = min(f[i][j], f[i][k] + 2 + m[(l + 1) / len]);
ans[i][j] = sub;
}
else if (f[i][j] == f[i][k] + 2 + m[(l + 1) / len]) ans[i][j] = max(ans[i][j], sub);//如果个数相同,找到字典序最大的一项。
}
}
}
}
cout << ans[1][n] << '\n';
}
return 0;
}

本文作者:luckycloud

本文链接:https://www.cnblogs.com/luckycloud/p/17810472.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   lucky_cloud  阅读(12)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起