[CODEVS 3037] 线段覆盖 5
描述
数轴上有n条线段,线段的两端都是整数坐标,坐标范围在0~10^18,每条线段有一个价值,请从n条线段中挑出若干条线段,使得这些线段两两不覆盖(端点可以重合)且线段价值之和最大。
分析
提供两种思路:
利用离散化. 因为这道题本来就是离散化的例题. 将点排序后依次赋值(1~2n, n为线段的条数), 再通过结构体里的信息将离散化后的点的坐标映射到线段上.
利用二分法直接DP. 看到题解里有二分两个字, 自己YY出一套二分的做法不过好像和题解里的不一样: 将线段按终点从小到大排序, 考虑线段i, 用 f[i][0] 记录不选 i 的最大权值和, f[i][1] 记录选择 i 的最大权值和. 那么显然有 f[i][0] = max{f[i-1][0], f[i-1][1]}. 然后二分 (upper_bound()) 查找最后一个可以接在 i 前的线段 j, 那么 f[i][1] = max{f[j][0], f[j][1]} + v[i].
不过尴尬的是第二种方法WA了四个点…而且差了不是一个数量级…
代码
3791ms, 65MB
// 1. 离散化
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 1000000 + 10;
LL f[maxn<<1];
struct Line {
int s, t, v;
bool operator < (const Line& rhs) const {
if(t != rhs.t) return t < rhs.t;
if(s != rhs.s) return s < rhs.s;
return v < rhs.v;
}
}lines[maxn];
struct Point {
bool d; // d=0 -> on left; d=1 -> on right
LL pos; // previous position
int id, x; // id of the line, current position
bool operator < (const Point& rhs) const {
return pos < rhs.pos;
}
}points[maxn<<1];
int main() {
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++) {
LL s, t;
scanf("%lld%lld%d", &s, &t, &lines[i].v);
points[i<<1] = (Point) {0, s, i};
points[(i<<1)^1] = (Point) {1, t, i};
}
sort(points, points + 2*n);
// discretization
int base = 0, cnt = 0;
while(base < 2*n) {
int cur = base;
points[cur++].x = ++cnt;
while(points[cur].pos == points[base].pos && cur < 2*n) points[cur++].x = cnt;
base = cur;
}
for(int i = 0; i < 2*n; i++) {
Point& P = points[i];
if(P.d == 0) lines[P.id].s = P.x;
else lines[P.id].t = P.x;
}
sort(lines, lines + n);
LL ans = 0LL;
for(int cur = 0, pre = 0; cur < n; cur++) {
Line& L = lines[cur];
if(L.t != pre) {
for(int i = pre + 1; i <= L.t; i++) f[i] = f[pre];
pre = L.t;
}
ans = max(ans, (f[L.t] = max(f[L.t], f[L.s] + L.v)));
}
printf("%lld\n", ans);
return 0;
}
// 二分
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1000000 + 10;
ll f[maxn][2], t[maxn];
struct Line {
ll s, t, v;
bool operator < (const Line& rhs) const {
if(t != rhs.t) return t < rhs.t;
if(v != rhs.v) return v > rhs.v;
return s > rhs.s;
}
}lines[maxn];
int main() {
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++) {
ll s, t, v;
scanf("%lld%lld%lld", &s, &t, &v);
lines[i] = (Line) {s, t, v};
}
sort(lines, lines + n);
for(int i = 0; i < n; i++) t[i] = lines[i].t; // 用于二分查找
f[0][0] = 0; f[0][1] = lines[0].v;
for(int i = 1; i < n; i++) {
Line L = lines[i];
f[i][0] = max(f[i-1][0], f[i-1][1]);
int j = upper_bound(t, t + n, L.s) - t - 1;
if(j >= 0) f[i][1] = max(f[j][0], f[j][1]) + L.v;
else f[i][1] = L.v; // 前面没有可以和它拼接的线段
}
printf("%d\n", max(f[n-1][0], f[n-1][1]));
return 0;
}