自从多校后心憔悴啊,发现DP还是太水了,有一场的区间DP竟然不会做,咳,果然是赤裸裸的水军。

花了几天时间写了几道区间DP的题目,大部分都是水题,然后和以前的合并起来就是KB区间DP这个8 + 1道题的专辑,大家可以试着AK。

区间DP是一类在区间上进行动态规划的最优问题,一般是根据问题设出一个表示状态的dp,可以是二维的也可以是三维的,一般情况下为二维。然后将问题划分成两个子问题,也就是一段区间分成左右两个区间,然后将左右两个区间合并到整个区间,或者说局部最优解合并为全局最优解,然后得解。

这类DP可以用常规的for循环来写,也可以用记忆化搜索来写,个人更倾向于记忆化搜索写法,因为这种写法相当易懂,要什么值你直接去记忆化搜索一下就ok了,随叫随到随用啊。

1、ZOJ 3537 Cake

第一次写的 凸包结合 DP,调了好久,

问题属于经典的三角剖分

区间DP状态转移方程:

\[f[i][j] = min(f[i][k] + f[k][j] + cost[i][k] + cost[k][j]
\]

其中 \(j >= i+ 3,i+1<=k<=j-1,cost[i][k]\) 为连一条 \(i\) 到 \(k\) 的线的费用

详细题解:Here

2、LightOJ 1422 Halloween Costumes

本题不可以直接模拟栈是因为,不知道当前的衣服本来就有还是需要新穿一件。

初始化DP为一直穿衣服

for (int i = 1; i <= n; ++i) for (int j = i; j <= n; ++j)
f[i][j] = f[i][j - 1] + (j == i || a[j] != a[j - 1]);

然后从按区间DP板子,枚举 \(k\) ,只要 \(a[k]\) 与 \(a[j]\) 相同说明,是可以把从 \(k+1\) 到 \(j-1\) 的所有衣服都脱掉,然后 \(k\) 天的这件衣服,直接在第 \(j\) 天参加聚会,就不需要穿新的衣服。从小区间依次递推到大区间,就可以得到正确的答案。

\[f[i][j] = f[i][j - 1] (a[i] == a[j]) \\
f[i][j] = min(f[i][k] + f[k + 1][j]) (a[i] == a[k]\ and\ i\le k\le j)
\]
const int N = 110;
int a[N], n, Case, f[N][N];
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
int _; for (cin >> _; _--;) {
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i)
for (int j = i; j <= n; ++j) f[i][j] = f[i][j - 1] + (j == i || a[j] != a[j - 1]);
for (int len = 1; len <= n; ++len)
for (int i = 1; i + len <= n; ++i) {
int j = i + len;
for (int k = i; k + 1 <= j; ++k)
if (a[k] == a[j]) f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j - 1]);
else f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j - 1] + 1);
}
cout << "Case " << ++Case << ": " << f[1][n] << "\n";
memset(f, 0, sizeof(f));
}
}

3、POJ 2955 Brackets

先初始化一下匹配括号,一对匹配的括号肯定是他内部的答案加上2,然后再考虑一坨括号能不能由两坨括号拼接而成,如果可以,那么取他们的最大值。简而言之,本题就是两个条件,逐步去实现他们即可。

const int N = 110;
int f[N][N];
string s;
bool match(char a, char b) {
if ((a == '(' && b == ')') || (a == '[' && b == ']')) return true;
return false;
}
int main() {
while (cin >> s) {
if (s == "end") break;
memset(f, 0, sizeof(f));
int n = int(s.size());
for (int len = 1; len < s.size(); ++len)
for (int i = 0; i + len < s.size(); ++i) {
int j = len + i;
if (match(s[i], s[j]))
f[i][j] = f[i + 1][j - 1] + 2;
for (int p = i; p <= j; ++p)
f[i][j] = max(f[i][j], f[i][p] + f[p + 1][j]);
}
cout << f[0][s.size() - 1] << "\n";
}
}

4、CF 149 D Coloring Brackets

这题是上一个题目的升级版。

题面有三个要求:

1.匹配的括号必须涂其中一个并且不能都涂

2.相邻两个括号不能都染色,但是都可以不染色

const int mod = 1e9 + 7, N = 800;
string s;
stack<int>st;
ll m[N], f[N][N][3][3], n;
ll dfs(int a, int b, int c, int d) {
//a表示左端点位置 b表示右端点位置 c表示左端点颜色 d表示右端点颜色
//0表示没涂色 1表示涂了一种颜色 2表示涂了另一种颜色
ll res = 0;
if (b <= a || f[a][b][c][d] != -1) { //如果区间已经被分的不能再分,或者上次已经算过,返回
if (b <= a)//只有一种方案
return 1;
else
return f[a][b][c][d];
}
if (b == m[a]) { //遇到这个区间左右端点恰好匹配 那么左右端点的方案已经固定了 向内部推一层
if (c != 1) res += dfs(a + 1, b - 1, 1, 0) % mod;
//如果左端点颜色不为1,则内部相邻的左端点颜色可以为1,左端点涂了色,右端点一定不能涂色
if (d != 1) res += dfs(a + 1, b - 1, 0, 1) % mod;
//如果右端点颜色不为1,则同理
if (c != 2) res += dfs(a + 1, b - 1, 2, 0) % mod;
//如果左端点颜色不为2,则同理
if (d != 2) res += dfs(a + 1, b - 1, 0, 2) % mod;
//如果右端点颜色不为2,则同理
} else {
//这个区间左右端点不匹配,则有四种情况
res += (dfs(a + 1, m[a] - 1, 0, 1) * dfs(m[a] + 1, b, 1, d)) % mod;
res += (dfs(a + 1, m[a] - 1, 0, 2) * dfs(m[a] + 1, b, 2, d)) % mod;
ll p = dfs(m[a] + 1, b, 0, d);
if (c != 1) res += (p * dfs(a + 1, m[a] - 1, 1, 0)) % mod;
if (c != 2) res += (p * dfs(a + 1, m[a] - 1, 2, 0)) % mod;
}
f[a][b][c][d] = res % mod;//记录已经算好的
return res % mod;
}
int main() {
// cin.tie(nullptr)->sync_with_stdio(false);
while (getline(cin, s)) {
n = s.size();
for (int i = 0; i < n; ++i) {
if (s[i] == '(') st.push(i);
else {
m[st.top()] = i;
st.pop();
}
}
memset(f, -1, sizeof(f)); // 进行记忆化搜索之前,将dp数组置为-1,避免有0的方案出现却未被计算
cout << (dfs(0, n - 1, 3, 3)) << "\n";
}
}

5、1651 Multiplication Puzzle

由于题目说了,最左最右是不可以取走的,那么我们只用对第2到n-1这个区间的数进行讨论即可。

对于样例模拟一下拿数,拿数有以下顺序:

2 3 4

2 4 3

3 2 4

3 4 2

4 2 3

4 3 2

可见,拿数的方式并不一定是连续的。

所以,先初始化一下长度为1的区间,答案就是他本身乘以他旁边的两个数,合并区间的时候可能是两个连续的区间合并,也有可能是两个断开的区间再把断点对应的得分带上(断点位置最后拿),即可满足无后效性

const int N = 110;
ll f[N][N], a[N], n;
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 2; i <= n - 1; ++i) f[i][i] = a[i] * a[i - 1] * a[i + 1];
for (int len = 1; len <= n; ++len)
for (int i = 2; i + len <= n; ++i) {
int j = len + i;
f[i][j] = min(f[i + 1][j] + a[i] * a[i - 1] * a[j + 1], f[i][j - 1] + a[j] * a[i - 1] * a[j + 1]);
for (int k = i + 1; k <= j - 1; ++k)
f[i][j] = min(f[i][j], f[i][k - 1] + f[k + 1][j] + a[k] * a[i - 1] * a[j + 1]);
}
cout << f[2][n - 1] << "\n";
}

6、ZOJ 3469 Food Delivery

可以把所有点分为两组,一组是在起点左边的,一组是在起点右边的,用f[i][j][k]表示左边的送完了i个,右边的送完了j个,当前停留在k组(0表示左边那组,1表示右边那组),然后用记忆化搜索来进行递推

const int N = 1e3 + 10, inf = 0x3f3f3f3f;
struct node {int x, b;} pz[N], py[N]; //左边的点靠右排前
bool cmp(node a, node b) {return a.x < b.x;}
//左边的点走了第几个 右边的点走了第几个 如今停留在右边还是左边
ll f[N][N][2];
ll dp(int i, int j, int k) {
if (f[i][j][k] != -1) return f[i][j][k];
ll ans = inf;
if (k == 0) { // 往左边
if (i != 0) {
int tmp = pz[i].b + py[j + 1].b;//所有还未走的的人的愤怒值之和
//cout << tmp << endl;
ans = min(ans, dp(i - 1, j, 0) + (pz[i].x - pz[i - 1].x) * tmp);
ans = min(ans, dp(i - 1, j, 1) + (pz[i].x + py[j].x) * tmp);
}
} else {
if (j != 0) {
int tmp = pz[i + 1].b + py[j].b;
ans = min(ans, dp(i, j - 1, 1) + (py[j].x - py[j - 1].x) * tmp);
ans = min(ans, dp(i, j - 1, 0) + (pz[i].x + py[j].x) * tmp);
}
}
return f[i][j][k] = ans;
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
int n, v, x;
while (cin >> n >> v >> x) {
int idz = 0, idy = 0;
for (int i = 1; i <= n; ++i) {
int tx, tb;
cin >> tx >> tb;
//直接存储相对位置
if (tx < x) pz[++idz].x = x - tx, pz[idz].b = tb;
if (tx > x) py[++idy].x = tx - x, py[idy].b = tb;
}
sort(pz + 1, pz + 1 + idz, cmp);
sort(py + 1, py + 1 + idy, cmp);
py[idy + 1].b = 0;
pz[idz + 1].b = 0;
for (int i = idy; i >= 1; --i) py[i].b += py[i + 1].b;
for (int i = idz; i >= 1; --i) pz[i].b += pz[i + 1].b;
memset(f, -1, sizeof(f));
f[0][0][0] = f[0][0][1] = 0;
cout << (v * min(dp(idz, idy, 0), dp(idz, idy, 1))) << "\n";
}
}

7、HDU 4283 You Are the One (较难)

8、Sdut 不老的传说问题 (较难)

9、HDU String painter

KB专题:区间DP专辑的更多相关文章

  1. kuangbin专题十二 POJ3186 Treats for the Cows (区间dp)

    Treats for the Cows Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7949   Accepted: 42 ...

  2. 「kuangbin带你飞」专题二十二 区间DP

    layout: post title: 「kuangbin带你飞」专题二十二 区间DP author: "luowentaoaa" catalog: true tags: - ku ...

  3. 区间dp专题练习

    区间dp专题练习 题意 1.Equal Sum Partitions ? 这嘛东西,\(n^2\)自己写去 \[\ \] \[\ \] 2.You Are the One 感觉自己智力被吊打 \(dp ...

  4. [kuangbin带你飞]专题二十二 区间DP

            ID Origin Title   17 / 60 Problem A ZOJ 3537 Cake   54 / 105 Problem B LightOJ 1422 Hallowee ...

  5. 专题训练之区间DP

    例题:以下例题部分的内容来自https://blog.csdn.net/my_sunshine26/article/details/77141398 一.石子合并问题 1.(NYOJ737)http: ...

  6. UESTC 2015dp专题 A 男神的礼物 区间dp

    男神的礼物 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/show/65 Descri ...

  7. 【专题】区间dp

    1.[nyoj737]石子合并 传送门:点击打开链接 描述    有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这 ...

  8. 区间dp专题

    HDU4283You Are the One区间dp, 记忆话搜索运行时间:   #include <iostream> #include <cstdio> #include ...

  9. hdu4597 区间dp

    //Accepted 1784 KB 78 ms //区间dp //dp[l1][r1][l2][r2] 表示a数列从l1到r1,b数列从l2到r2能得到的最大分值 // #include <c ...

  10. hdu4283 区间dp

    //Accepted 300 KB 0 ms //区间dp //dp[i][j] 表示i到j第一个出场的最小diaosizhi //对于i到j考虑元素i //(1)i第一个出场,diaosizhi为 ...

随机推荐

  1. Windows_Cmd常用操作配置

    目录 特定功能执行命令 显示系统当前版本 电源管理 历史命令相关 显示路由表 显示本地 ARP 缓存 测试主机 联通性 查看网卡信息 修改DOS窗口中的编码格式 诊断域名系统 (DNS) 基础结构的信 ...

  2. java协程操作mysql数据库

    我的项目: nanshaws/nettyWeb: 复习一下netty,并打算做一个web项目出来 (github.com) 最近在项目中分别添加了虚拟线程操作mysql数据库,和用协程操作mysql数 ...

  3. python数据类型元组、列表、集合、字典相互嵌套

    系统 Windows 10 专业工作站版22H2 软件 python-3.9.6-amd64.exe 拓展库: jupyter==1.0.0 notebook==7.0.6 1.元组嵌套 1.1 元组 ...

  4. macOS 苹果电脑双面打印单面打印PDF设置

    苹果的打印服务分为两个部分,一个是应用层另一个是系统层. 其中双面打印或单面打印统一在系统层面设置,下面我分别截图示意wps pdf和福昕pdf两款软件设置双面打印. 1.WPS PDF 在完成方式中 ...

  5. [NOI online2022普及A] 王国比赛

    题目描述 智慧之王 Kri 统治着一座王国. 这天 Kri 决定举行一场比赛,来检验自己大臣的智慧. 比赛由 \(n\) 道判断题组成,有 \(m\) 位大臣参加.现在你已经知道了所有大臣的答题情况, ...

  6. springboot——入门案例

    真简单啊 springboot 学了入门案例,有感而发 首先是一个自带的配置文件: package com.example.springboot_01; import org.springframew ...

  7. 8 HTTP 的请求方法

    目录 标准请求方法 GET/HEAD GET 方法 HEAD方法 POST/PUT POST PUT 非常用方法 DELETE 方法 CONNECT 方法 OPTIONS 方法 TRACE 方法 拓展 ...

  8. Cocos内存管理解析 CCRef/retain/release/autorelease

    Cocos内存管理源码(autorelease解析) 背景 这段时间在做项目的时候,需求需要往spine动作的挂点上绑定按钮节点,由于按钮在编辑器中是加在已有节点上的,所以在往spine上添加挂点时, ...

  9. 一款基于.NET Core的快速开发框架、支持多种前端UI、内置代码生成器

    前言 经常看到有小伙伴在技术群里问有没有什么好用且快速的开发框架推荐的,今天就给大家分享一款基于MIT License协议开源.免费的.NET Core快速开发框架.支持多种前端UI.内置代码生成器. ...

  10. Javascript Ajax总结——其他跨域技术之Web Sockets

    Web Sockets的目标是在一个单独的持久连接上提供全双工.双向通信.在Javascript中创建了Web Sockets之后,会有一个HTTP请求发送到浏览器以发起连接.在取得服务器响应后,建立 ...