[CODEVS 3037] 线段覆盖 5

描述

数轴上有n条线段,线段的两端都是整数坐标,坐标范围在0~10^18,每条线段有一个价值,请从n条线段中挑出若干条线段,使得这些线段两两不覆盖(端点可以重合)且线段价值之和最大。


分析

提供两种思路:

  1. 利用离散化. 因为这道题本来就是离散化的例题. 将点排序后依次赋值(1~2n, n为线段的条数), 再通过结构体里的信息将离散化后的点的坐标映射到线段上.
  2. 利用二分法直接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;
}
时间: 2024-10-25 01:53:05

[CODEVS 3037] 线段覆盖 5的相关文章

codevs 1643 线段覆盖 3

1643 线段覆盖 3 时间限制: 2 s 空间限制: 256000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 在一个数轴上有n条线段,现要选取其中k条线段使得这k条线段两两没有重合部分(端点可以重合),问最大的k为多少. 输入描述 Input Description 输入格式 输入文件的第1行为一个正整数n,下面n行每行2个数字ai,bi,描述每条线段. 输出描述 Output Description 输出格式 输出文件仅包括1个整数,为k的最大值 样例输入 

codevs 1214 线段覆盖

1214 线段覆盖 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 给定x轴上的N(0<N<100)条线段,每个线段由它的二个端点a_I和b_I确定,I=1,2,……N.这些坐标都是区间(-999,999)的整数.有些线段之间会相互交叠或覆盖.请你编写一个程序,从给出的线段中去掉尽量少的线段,使得剩下的线段两两之间没有内部公共点.所谓的内部公共点是指一个点同时属于两条线段且至少在其中一条线段的内部(即除去端点的部分). 输入描

codevs 1214线段覆盖

1214 线段覆盖 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 给定x轴上的N(0<N<100)条线段,每个线段由它的二个端点a_I和b_I确定,I=1,2,--N.这些坐标都是区间(-999,999)的整数.有些线段之间会相互交叠或覆盖.请你编写一个程序,从给出的线段中去掉尽量少的线段,使得剩下的线段两两之间没有内部公共点.所谓的内部公共点是指一个点同时属于两条线段且至少在其中一条线段的内部(即除去端点的部分). 输入描

CodeVS 3027 线段覆盖2

题目大意: http://codevs.cn/problem/3027/ 源码: #include <iostream> using namespace std; struct { int x,y,val; }tmp[1050]; int dp[1050] = {0}; int main() { int n; cin >> n ; for(int i = 1; i <= n; i++) { cin >> tmp[i].x >> tmp[i].y >

CODEVS3037 线段覆盖 5[序列DP 二分]

3037 线段覆盖 5  时间限制: 3 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description 数轴上有n条线段,线段的两端都是整数坐标,坐标范围在0~10^18,每条线段有一个价值,请从n条线段中挑出若干条线段,使得这些线段两两不覆盖(端点可以重合)且线段价值之和最大. 输入描述 Input Description 第一行一个整数n,表示有多少条线段. 接下来n行每行三个整数, ai bi ci,分别代表第i条线段的左端点ai,右端点b

【codevs】【DP】3027线段覆盖2

题目描述 Description 数轴上有n条线段,线段的两端都是整数坐标,坐标范围在0~1000000,每条线段有一个价值,请从n条线段中挑出若干条线段,使得这些线段两两不覆盖(端点可以重合)且线段价值之和最大. n<=1000 输入描述 Input Description 第一行一个整数n,表示有多少条线段. 接下来n行每行三个整数, ai bi ci,分别代表第i条线段的左端点ai,右端点bi(保证左端点<右端点)和价值ci. 输出描述 Output Description 输出能够获得

【BZOJ-3589】动态树 树链剖分 + 线段树 + 线段覆盖(特殊的技巧)

3589: 动态树 Time Limit: 30 Sec  Memory Limit: 1024 MBSubmit: 405  Solved: 137[Submit][Status][Discuss] Description 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0: 这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1: 小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝

【基础练习】【贪心】codevs1214 线段覆盖题解

题目:已知数轴上0<N<10000条线段.每条线段按照端点Ai和Bi(Ai<>Bi,i=1..N)定义.端点坐标在(-999,999)内,坐标为整数.有些线段可能相交.编程实现删除最少数目的线段,使得余下的任意两条线段不相交. codevs和洛谷数据范围不同 改一下常量maxn即可 思路是很简单的贪心 //codevs1214 线段覆盖 贪心 //先排序,扫描一遍,每次保留右端点最小的线段,重叠的delete,画一张图即可证明 #include<cstdio> #inc

cogs265.线段覆盖

265. 线段覆盖 ★★★☆   输入文件:xdfg.in   输出文件:xdfg.out   简单对比 时间限制:2 s   内存限制:20 MB [问题描述] 有一根长度为 L 的白色条状物.有两种操作: 用一条长度为 T 的黑布盖住条状物的 [a, a+T] 这个区间 (0<=a, T<=L) . 把某条黑布拿走. 输入 L 和 n 次操作,要你输出每次操作之后: 条状物上有多少个黑区间. 条状物上黑区间的总长度. [输入格式] 输入文件第一行两个整数L(1<=L<=2000