【2019.8.14】2019QB学堂DP图论班第一次考试 Problem C

Problem C

Problem Description

Tarzan 非常烦数轴因为数轴上的题总是难度非常大。不过他非常喜欢线段,因为有关线
段的题总是不难,讽刺的是在一个数轴上有 n 个线段,Tarzan 希望自己喜欢的东西和讨厌的
东西不在一起,所以他要把这些线段分多次带走,每一次带走一组,最多能带走 k 次。其实
就是要把这些线段分成至多 k 组,每次带走一组,问题远没有那么简单,tarzan 还希望每次
选择的线段组都很有相似性,我们定义一组线段的相似性是组内线段交集的长度,我们现在
想知道最多分成 k 个组带走,Tarzan 最多能得到的相似性之和是多少?

Input format

第一行两个整数 n 和 k。
接下来 n 行每行两个整数 Li, Ri 表示线段的左右端点。

Output format

一行一个整数,表示最多能得到的相似性之和是多少。

Examples

input 1

5 3
5 10
4 11
6 9
10 30
20 40

output 1

43

input 2

5 3
5 11
16 22
14 20
10 20
6 10

output 2

18

input 3

7 3
1 9
2 9
2 10
5 15
3 14
14 18
16 20

output 3

21

Constrains and Notes

对于 20% 的数据满足:\(n ≤ 8; k ≤ 5\)
对于 40% 的数据满足:\(k, n ≤ 12\);
对于 70% 的数据满足:\(n ≤ 100\),
对于 100% 的数据满足:\(1 ≤ k ≤ n ≤ 6000, 1 ≤ Li < Ri ≤ 10^6\);

题解

首先,手玩样例一番,收集性质如下:

①空集组(对答案无贡献)最多只有一组。

证明:若有\(k(k >= 2)\)组空集, 则在这些集合中找出前\(k-1\)条线段放入一个集合, 另外的线段放入剩下的一个集合可增加选出来的\(k-1\)条线段长度的答案贡献。

性质①得证。

②若没有空集的话,可以再观察到一个性质:

对于完全包含另一个线段B的线段A, 则B与A在一组可能会使答案更优(但不一定),不优的情况会在下文另行考虑。

证明:根据题意可得,对于给出的\(n\)条线段,每条线段都会属于一个集合。

设长度为\(x\)的线段被长度为\(y(y > x)\)的线段包含。

因 \(x\)被\(y\)包含,所以\(x\)与\(y\)分到一组,则\(x\)所在集合对答案的贡献最大为\(x\)。

而若\(x\)与不包含\(x\)的线段分到一组时,在小的方面来说答案不优(整体来看可能更优)。

性质②得证。

下面对于性质②的缺陷作考虑:

性质②是将\(x\)与\(y\)放到一个集合,那我们未考虑到的情况就是\(x\)与\(y\)相分离的情况。

我们考虑\(x\)与\(y\)相分离时怎样最优。

显然当\(y\)单独一个集合时对答案的贡献最大。

当然,上面情况成立的条件是仍有空的集合未被使用。

注:当产生包含关系后,\(y\)对集合已无贡献。

所以我们才可以将\(x\)与\(y\)进行分离操作。

否则,算法正确性无法得以保证。

此时,再结合性质①,对答案无贡献的集合最多只有一个。

我们便有了一种做法:

优先考虑不包含的情况,再将\(x\)与\(y\)进行分离操作,更新答案。

因分离线段操作(只考虑包含别的线段的线段)不会对现有答案产生影响。

所以我们可以进行DP预处理操作。

将线段排序,挑选所有出\(r\)递增且\(l\)递增(不包含)的线段。

此时显然能DP。

设\(f[i][j]\)为前\(i\)条线段选了\(j\)个集合相似度之和。

注:这\(j\)个集合不能有空集,因为我们要给每个空集分发线段,更新答案。

否则答案不优。

考虑第\(j\)个集合放入第\(x+1\sim i\)条线段,

则\(f[i][j]=max(f[x][j-1]+r[x+1]-l[i]|r[x+1]>l[i])\)

转化方程得:\(f[i][j]=max(f[x][j-1]+r[x+1]|r[x+1]>l[i])-l[i]\)

进行单调队列优化。

然后考虑我们选择了多少集合,设为\(p\),

考虑用包含其它线段的线段来更新答案。

则此情况对答案的贡献为

\(f[n][p]\)+包含其它线段的前\(k-p\)长线段长度之和。

再枚举\(p\)取最优值即可。

时间复杂度:\(O(n^2)\),在空间上可以使用滚动数组优化。

code(注释版本)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 6e3 + 5;
int read() {
    int x = 0, f = 1; char ch = getchar();
    while(! isdigit(ch)) f = (ch=='-')?-1:1, ch = getchar();
    while(isdigit(ch)) x = (x<<3)+(x<<1)+(ch^48), ch = getchar();
    return x * f;
}
struct edge { int l, r; } t1[N], t2[N];
int n, k, ans, r, len[N], q[N], head, tail, maxl, tot, cnt, f[2][N];
bool cop(const edge &a, const edge &b) {
    return (a.r == b.r) ? (a.l > b.l) : (a.r < b.r);
}
int main() {
    n = read(); k = read();
    for(int i = 1;i <= n;i ++)
        t1[i].l = read(), t1[i].r = read(), len[i] = t1[i].r - t1[i].l;
    sort(len + 1, len + n + 1);
    for(int i = n-k+2;i <= n;i ++) ans +=len[i];//共前 k-1 长 (总前缀和);
    sort(t1 + 1, t1 + n + 1, cop);
    memset(len, 0, sizeof(len));
    for(int i = 1;i <= n;i ++) {//保证r递增
        if(t1[i].l > maxl) {//若l递增 (这些线段不可互相包含)
            t2[++ cnt] = t1[i];//加入t2(用于DP)
            maxl = t1[i].l;
        }
        else len[++ tot] = t1[i].r - t1[i].l;//r递增l递减,加入t1(说明这些线段有包含关系)
    }
    sort(len + 1, len + tot + 1, greater<int>());//长度从小到大排序
    for(int i = 2;i <= n;i ++) len[i] += len[i-1];//求包含其它线段的线段的长度前缀和
    sort(t2 + 1, t2 + cnt + 1, cop);//这些线段不相互包含
    r = 1;//滚动数组优化
    for(int i = 1;i <= cnt;i ++) {//保证r递增(已经排序)
        if(t2[1].r <= t2[i].l) f[0][i] = -1e9;//1号线段与i号线段为空集
        else f[0][i] = t2[1].r - t2[i].l;//1号线段与i号线段有交集
    }
    ans = max(ans, f[0][cnt] + len[k-1]);//1个集合 + (k - 1)个集合
    for(int j = 2;j <= min(k, cnt);j ++, r ^= 1) {//枚举选出j个集合,(j < k)
        q[head = tail = 1] = 1; f[r][1] = -1e9;//滚动数组清零
        for(int i = 2;i <= cnt;i ++) {
            while(head <= tail&&t2[q[head]+1].r <= t2[i].l) head ++;//到下一个集合块部分
            if(head <= tail) f[r][i] = f[r^1][q[head]]+t2[q[head]+1].r-t2[i].l;
            else f[r][i] = -1e9;
            while(head <= tail&&f[r^1][i]- f[r^1][q[tail]] >= t2[q[tail]+1].r - t2[i+1].r) tail --;
            q[++ tail] = i;
        }
        ans = max(ans, f[r][cnt] + len[k - j]);//j个集合 + (k - j) 个集合
    }
    cout << ans << endl;
    return 0;
}

code(蓝光无注释版本)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 6e3 + 5;
int read() {
    int x = 0, f = 1; char ch = getchar();
    while(! isdigit(ch)) f = (ch=='-')?-1:1, ch = getchar();
    while(isdigit(ch)) x = (x<<3)+(x<<1)+(ch^48), ch = getchar();
    return x * f;
}
struct edge { int l, r; } t1[N], t2[N];
int n, k, ans, r, len[N], q[N], head, tail, maxl, tot, cnt, f[2][N];
bool cop(const edge &a, const edge &b) {
    return (a.r == b.r) ? (a.l > b.l) : (a.r < b.r);
}
int main() {
    n = read(); k = read();
    for(int i = 1;i <= n;i ++)
        t1[i].l = read(), t1[i].r = read(), len[i] = t1[i].r - t1[i].l;
    sort(len + 1, len + n + 1);
    for(int i = n-k+2;i <= n;i ++) ans +=len[i];
    sort(t1 + 1, t1 + n + 1, cop);
    memset(len, 0, sizeof(len));
    for(int i = 1;i <= n;i ++) {
        if(t1[i].l > maxl) {
            t2[++ cnt] = t1[i];
            maxl = t1[i].l;
        }
        else len[++ tot] = t1[i].r - t1[i].l;
    }
    sort(len + 1, len + tot + 1, greater<int>());
    for(int i = 2;i <= n;i ++) len[i] += len[i-1];
    sort(t2 + 1, t2 + cnt + 1, cop);
    r = 1;
    for(int i = 1;i <= cnt;i ++) {
        if(t2[1].r <= t2[i].l) f[0][i] = -1e9;
        else f[0][i] = t2[1].r - t2[i].l;
    }
    ans = max(ans, f[0][cnt] + len[k-1]);
    for(int j = 2;j <= min(k, cnt);j ++, r ^= 1) {
        q[head = tail = 1] = 1; f[r][1] = -1e9;
        for(int i = 2;i <= cnt;i ++) {
            while(head <= tail&&t2[q[head]+1].r <= t2[i].l) head ++;
            if(head <= tail) f[r][i] = f[r^1][q[head]]+t2[q[head]+1].r-t2[i].l;
            else f[r][i] = -1e9;
            while(head <= tail&&f[r^1][i]- f[r^1][q[tail]] >= t2[q[tail]+1].r - t2[i+1].r) tail --;
            q[++ tail] = i;
        }
        ans = max(ans, f[r][cnt] + len[k - j]);
    }
    cout << ans << endl;
    return 0;
}

原文地址:https://www.cnblogs.com/Paranoid-LS/p/11354649.html

时间: 2024-07-31 15:52:22

【2019.8.14】2019QB学堂DP图论班第一次考试 Problem C的相关文章

QBXT2018 4 29 DP&amp;图论班 胡策 by zhx

PS:由于长者是以我们没有学过图论与DP的基础上出的题,所以可能(?)会有点水. 题意: T1:判断括号序列是是否合法.可以利用栈,别忘了判断最终栈是否为空. #include<cstdio> #include<cstdlib> #include<cstring> using namespace std; char s[1000],z[1000]; int size; int main() { scanf("%s",s+1); int l=strle

QBXT 2018春季DP&amp;图论班 2018.5.3 --- 区间DP专题

本文题目等来自北京大学张浩威学长的PPT. 1.区间DP:解决有关两个或以上的区间的合并或删除的问题(最大/小次数/价值.方案总数.可行性等). 2.石子合并: 有n堆石子排成一排,第i堆石子的个数为ai.每次可以将相邻两堆合并成一堆.合并的代价为两堆石子的石子个数之和.设计方案要求代价之和最小. 状态:dp[l][r]表示只考虑区间l~r的石子,将它们合并的最小代价. 状态转移:dp[l][r]=min{dp[l][r],dp[l][k]+dp[k+1][r]+s[r]-s[l-1]} l~r

DP&amp;图论 DAY 5 下午

DP&图论  DAY 5  下午 树链剖分  每一条边要么属于重链要么轻边 证明: https://www.cnblogs.com/sagitta/p/5660749.html 轻边重链都是交替走的(此处重链可以走若干条边) 1.dfs1 统计子树大小,确定重儿子 2.dfs2 找重链       重链,子树,分别是连续的一段 每个结点属于一个重链 ta < tb a 跳到 ta 的父节点 logn 级别 将树序列化 SPOJ QTREE Query on a tree Solution 树

DP&amp;图论 DAY 6 下午 考试

DP&图论  DAY 6  下午  考试 3 5 10 3 1 3 437 1 2 282 1 5 328 1 2 519 1 2 990 2 3 837 2 4 267 2 3 502 3 5 613 4 5 132 1 3 4 10 13 4 1 6 484 1 3 342 2 3 695 2 3 791 2 8 974 3 9 526 4 9 584 4 7 550 5 9 914 6 7 444 6 8 779 6 10 350 8 8 394 9 10 3 7 10 9 4 1 2 3

[14.05.12]今后讨论班的走向

今后讨论班的一些走向 谨记之...... 上周末决定,今后讨论班将针对CVPR 2014和SIAM Conference on IMAGING SCIENCE(2014) 进行进一步的挖掘.上级给我们刚刚布置了SIAM会议的日程.内容安排,看完之后,顿觉亚历山大啊......会是好会,会址更是博主心仪之地.虽不能至,心向往之...... http://www.math.hkbu.edu.hk/SIAM-IS14/ 会议介绍里面有个词用的很好:from nano-scale to the astr

DP&amp;图论 DAY 3 上午

DP&图论  DAY 3  上午 状态压缩dp >状态压缩dp ?状态压缩是设计dp状态的一种方式.?当普通的dp状态维数很多(或者说维数与输入数据有关),但每一维总量很少是,可以将多维状态压缩为一维来记录.?这种题目最明显的特征就是: 都存在某一给定信息的范围非常小(在20以内),而我们在dp中所谓压缩的就是这一信息.?(或者是在做题过程中分析出了某一信息种类数很少)?我们来看个例子. >经典题?给出一个n*m的棋盘,要放上一些棋子,要求不能有任意两个棋子相邻.求方案数.? n<

DP&amp;图论 DAY 4 下午图论

DP&图论  DAY 4  下午 后天考试不考二分图,双联通 考拓扑排序 图论 图的基本模型 边: 有向边构成有向图 无向边构成无向图 权值: 1.无权 2.点权 3.边权 4.负权(dij不可以跑) 环: 1. 2.重边 3.有向无环图DAG 路径: 1.简单路径:不经过重复的点  1-->2-->3 不简单路径:经过重复点  1-->2-->3-->1-->4 2.连通,具有传递性 图: 1.树:n个点,n-1条边的无环连通图 2.完全图:一个无向图,图中任

七月算法--12月机器学习在线班-第一次课笔记—微积分与概率论

七月算法--12月机器学习在线班-第一次课笔记—微积分与概率论 七月算法(julyedu.com)12月机器学习在线班学习笔记 http://www.julyedu.com

【誉天】2019年11月HCIE-R&S学员LAB认证考试成绩单合集

誉天2019年11月HCIE&RS学员LAB认证考试成绩单合集 出炉啦! 祝贺这些学员,并希望这些学员在接下来的面试考试中发挥同样优异的表现! HCIE证书正在向大家招手~ 电话:4008-868-010 QQ:1424150046 微信:誉天教育(yutianeduzs) [誉天]2019年11月HCIE-R&S学员LAB认证考试成绩单合集 原文地址:https://blog.51cto.com/13561944/2456441