圆
圆
题目描述
给出一个圆,圆上等距分布 $n$ 个点,编号为 $1\sim n$。
另有 $m$ 条线段,第 $i$ 条线段的端点为 $x_i$ 和 ,权重为 $w_i$。
定义一个圆是优良的,当且仅当所有线段无交(端点重合也算相交)。
如上图,线段 $\{1\rightarrow 4\}$ 与线段 $\{2\rightarrow 5\},\{3\rightarrow 4\}$ 相交,但是线段 $\{2\rightarrow 5\}$ 与线段 $\{3\rightarrow 4\}$ 不交。
若删掉一条边的代价为其权重,求让圆优良的最小代价。
输入描述:
第一行两个数 $n,m$。接下来 $m$ 行,每行三个数 $x,y,w$,描述一条线段。
注意线段可能重合。
输出描述:
输出为一个数,即最小代价。
示例1
输入
6 4
1 4 1
2 3 3
2 4 10
5 6 2
输出
4
备注:
对于所有数据,$1\le n,m \le 2\times 10^3$,$0\le w_i \le 10^9$,$x_i\not=y_i$。
解题思路
来补一下这道题,当时卡了一个多小时才想到区间 dp 的做法,本来思路都是对的,结果初始化的时候不知道抽什么风搞得很复杂,最后 WA 到结束了都没做出来。
把圆上的线段看作区间,那么所有线段无交就等价于任意两个区间都不相交,但允许区间被完全包含(端点不能重合)。因此问题就变成了,删除若干个区间使得剩余的区间满足条件的最小代价。问题可以继续转换成,假设一开始没有区间,现在要在满足条件的前提下添加尽可能多的区间,求能添加的最大代价是多少。最后用所有区间的总代价减去这个值就是删除区间所需的最小代价。
如果要保留某个区间 $[l,r]$,那么所有选择的区间必须严格在 $[l+1,r-1]$,$[1, l-1]$,$[r+1,n]$ 这些范围内。都是区间的形式因此可以考虑用区间 dp。
定义 $f(l,r)$ 表示只选择 $[l,r]$ 范围内且满足条件的区间的所有方案中的最大代价。根据是否选择了左端点为 $l$ 的区间来进行状态划分。如果不选择左端点为 $l$ 的区间,那么状态转移方程就是 $f(l,r) = f(l+1,r)$。如果选择左端点为 $l$ 的区间,记 $y_i$ 和 $w_i$ 为对应区间的右端点和代价,只能选择 $y_i \leq r$ 的区间,状态转移方程为 $f(l,r) = \min\limits_{y_i \leq r}\left\{ f(y_i+1, r) + f(l+1, y_i-1) + w_i \right\}$。
最后只需初始化所有的 $f(l,r)$ 为 $0$。答案就是 $\sum\limits_{i=1}^{m}{w_i}-f(1,n)$。
AC 代码如下,时间复杂度为 $O(n^2)$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e3 + 10;
vector<array<int, 2>> g[N];
LL f[N][N];
int main() {
int n, m;
scanf("%d %d", &n, &m);
LL s = 0;
while (m--) {
int x, y, w;
scanf("%d %d %d", &x, &y, &w);
if (x > y) swap(x, y);
g[x].push_back({y, w});
s += w;
}
for (int len = 2; len <= n; len++) {
for (int i = 1; i + len - 1 <= n; i++) {
int j = i + len - 1;
f[i][j] = f[i + 1][j];
for (auto &p : g[i]) {
if (p[0] <= j) f[i][j] = max(f[i][j], f[p[0] + 1][j] + f[i + 1][p[0] - 1] + p[1]);
}
}
}
printf("%lld", s - f[1][n]);
return 0;
}
参考资料
牛客练习赛122 官方题解:https://ac.nowcoder.com/discuss/1255860
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18053991