最长 \(k\) 可重区间集

题目大意

给定实心直线 \(L\) 上 \(n\) 个开区间组成的集合 \(I\) ,和一个正整数 \(k\) ,试设计一个算法,从开区间集合 \(I\) 中选取开区间集合 \(S \subseteq I\) ,使得在实直线 \(L\) 上的任意一点 \(x\) , \(S\) 中包含 \(x\) 的开区间个数不超过 \(k\) ,且 \(\sum_{z\in S}|Z|\) 达到最大( \(|Z|\) 表示开区间 \(z\) 的长度)。

这样的集合 \(S\) 称为开区间集合 \(I\) 的最长 \(k\) 可重区间集。 \(\sum_{z\in S}|z|\) 称为最长 \(k\) 可重区间集的长度。

对于给定的开区间集合 \(I\) 和正整数 \(k\) ,计算开区间集合 \(I\) 的最长 \(k\) 可重区间集的长度。

分析

算法

既然是网络流 \(24\) 题,大家都清楚可以用网络流解决,下面还是说一下思维的过程。

拿到这道题首先注意到 \(n\) 的范围非常小,然后通过读题了解到本题是要我们从给出的一个线段集中选出一些线段,且线段的长度和最大,要求在 \(x\) 轴上的任意一点不得被超过 \(k\) 个线段覆盖。

而网络流算法正是一个很适合用流量来表示限制的算法,我们能够很轻松的表示出本题 \(k\) 的限制,并且所求的是一个最大值,这正是网络流算法很擅长解决的一个问题,这更加让人相信此题的解法要用到网络流,而要用网络流的话,我们就只需要考虑如何建图,然后跑模板即可。

建图

我们首先能够确定的是,所有的线段都只能被选择一次,那么就很自然的有了拆点的想法,把线段 \(i\) 拆成两个点,\(i\) 为入点, \(i'\) 为出点,再在 \(i\) 和 \(i'\) 间建一条边,至于这条边的各参数,还需要进行考虑。

拆点之后,怎么建边?如何表达出 \(k\) 的限制?

我们能够发现:

\(x\) 轴上任意一点不得被超过 \(k\) 个线段覆盖 \(\iff\) 从线段集中选出至多 \(k\) 组互不相交的线段

那我们就能够把问题转化为从线段集 \(I\) 中选出 \(k\) 组互不相交的线段,使得所有被选择的线段长度和最大。

还是老套路,接下来需要建立一个超级源点超级汇点。而超级源点需要连向源点,这条边的流量是 \(k\) ,到这儿,我们发现最终我们用流量来表示题目中的限制,很显然这个问题就需要用到费用流来解决了,当然超级源点到源点的花费是 \(0\) 。

同时,源点向每条边的入点连一条流量为 \(1\) ,费用为 \(0\) 的边,每条边的出点向超级汇点连一条流量为 \(1\) ,费用为 \(0\) 的边。

然后再回到拆点的连边上,对于拆点的连边,我们需要建立一条流量为 \(1\) ,费用为线段长度的边。

最后我们只需要对两条不相交的线段建一条流量为 \(+\infty\) ,费用为 \(0\) 的边即可。

想一想这样做为什么正确?

从超级源点到超级汇点的每一条路径,它所经过的边的最小流量必然为 \(1\) ,所以超级源点到源点提供的 \(k\) 个流量就能刚好供我们选择 \(k\) 条路径,而拆点保证了每个点只能被选择一次,所以这张图和题目完全相符,我们只需要跑最大费用流即可。

还有一个小细节,我们需要将所有边排序,无论是从大到小还是从小到大,其实也不一定要排序,我们只需要保证对于线段间的连边是有序的即可,否则就会出现如下图的情况:

很显然这样的连边是一定错误的,这条本来能够走通的路径是走不通的,所以我们有必要按照左端点的大小排序,杜绝此类情况的发生。

下面给出代码:

CODE

#include <bits/stdc++.h>
using namespace std;
const int N=6e2+10,INF=0x7fffffff;
struct node { int l,r; }sec[N];
int n,k,s,_s,t,ans;
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int len[N];
int tot=-1,v[N*N],w[N*N],p[N*N],nex[N*N],first[2*N];
inline void Add(int x,int y,int z,int c)
{
nex[++tot]=first[x];
first[x]=tot;
v[tot]=y,w[tot]=z,p[tot]=c;
}
bool vis[2*N];
int pre[2*N],dis[2*N],Min[2*N];
inline bool SPFA()
{
for(register int i=s;i<=t;i++) dis[i]=-INF;
for(register int i=s;i<=t;i++) vis[i]=false;
queue<int> q;
q.push(s);
vis[s]=1,dis[s]=0,Min[s]=INF;
while(!q.empty()){
int now=q.front(); q.pop();
vis[now]=false;
for(register int i=first[now];i!=-1;i=nex[i]){
int to=v[i];
if(!w[i]) continue;
if(dis[to]<dis[now]+p[i]){
dis[to]=dis[now]+p[i];
Min[to]=min(Min[now],w[i]);
pre[to]=i;
if(!vis[to]) q.push(to),vis[to]=true;
}
}
}
return dis[t]!=-INF;
}
inline void EK()
{
while(SPFA()){
ans+=dis[t]*Min[t];
int temp=t,i;
while(temp!=s){
i=pre[temp];
w[i]-=Min[t];
w[i^1]+=Min[t];
temp=v[i^1];
}
}
}
int main()
{
memset(first,-1,sizeof(first));
n=read(),k=read();
//以s为超级源点,_s为超级源点的拆点,t为超级源点的汇点
for(register int i=1;i<=n;i++){
sec[i].l=read(),sec[i].r=read();
if(sec[i].l>sec[i].r) swap(sec[i].l,sec[i].r);
}
s=0,_s=2*n+1,t=2*n+2;
Add(s,_s,k,0),Add(_s,s,0,0);
for(register int i=1;i<=n;i++){
//超级源点向每一个区间连边
Add(_s,i,1,0),Add(i,_s,0,0);
//每个区间的两个拆点之间连边
Add(i,i+n,1,sec[i].r-sec[i].l),Add(i+n,i,0,sec[i].l-sec[i].r);
//每个区间向超级汇点连边
Add(i+n,t,1,0),Add(t,i+n,0,0);
}
//两个不相交的区间可以相互连边
for(register int i=1;i<=n;i++){
for(register int j=i+1;j<=n;j++){
if(sec[j].l>=sec[i].r||sec[i].l>=sec[j].r){
int x1=i,x2=j;
if(sec[x1].l<sec[x2].l) swap(x1,x2);
Add(x1+n,x2,INF,0),Add(x2,x1+n,0,0);
}
}
}
EK();
printf("%d\n",ans);
return 0;
}

[网络流24题]最长k可重区间集[题解]的更多相关文章

  1. COGS743. [网络流24题] 最长k可重区间集

    743. [网络流24题] 最长k可重区间集 ★★★   输入文件:interv.in   输出文件:interv.out   简单对比时间限制:1 s   内存限制:128 MB «问题描述: «编 ...

  2. [网络流24题]最长k可重线段集[题解]

    最长 \(k\) 可重线段集 题目大意 给定平面 \(x-O-y\) 上 \(n\) 个开线段组成的集合 \(I\) ,和一个正整数 \(k\) .试设计一个算法,从开线段集合 \(I\) 中选取开线 ...

  3. [网络流24题] 最长k可重区间集

    https://www.luogu.org/problemnew/show/3358 以区间(1,5),(2,6),(7,8)为例 建模方法一: 建模方法二: 离散化区间端点 相当于找k条费用最大的不 ...

  4. [网络流24题] 最长K可重区间集问题

    题目链接:戳我 当时刷24题的时候偷了懒,没有写完,结果落下这道题没有写qwq结果今天考试T3中就有一部分要用到这个思想,蒟蒻我硬是没有想到网络流呜呜呜 最大费用流. 就是我们考虑将问题转化一下,转化 ...

  5. [网络流24题] 最长k可重区间集问题 (费用流)

    洛谷传送门 LOJ传送门 很巧妙的建图啊...刚了$1h$也没想出来,最后看的题解 发现这道题并不类似于我们平时做的网络流题,它是在序列上的,且很难建出来二分图的形. 那就让它在序列上待着吧= = 对 ...

  6. [网络流24题] 最长k可重线段集问题 (费用流)

    洛谷传送门 LOJ传送门 最长k可重区间集问题的加强版 大体思路都一样的,不再赘述,但有一些细节需要注意 首先,坐标有负数,而且需要开$longlong$算距离 但下面才是重点: 我们把问题放到了二维 ...

  7. 网络流24题-最长k可重线段集问题

    最长k可重线段集问题 时空限制1000ms / 128MB 题目描述 给定平面 x−O−y 上 n 个开线段组成的集合 I,和一个正整数 k .试设计一个算法,从开线段集合 I 中选取出开线段集合 S ...

  8. 【网络流24题】最长k可重区间集(费用流)

    [网络流24题]最长k可重区间集(费用流) 题面 Cogs Loj 洛谷 题解 首先注意一下 这道题目里面 在Cogs上直接做就行了 洛谷和Loj上需要判断数据合法,如果\(l>r\)就要交换\ ...

  9. LibreOJ #6014. 「网络流 24 题」最长 k 可重区间集

    #6014. 「网络流 24 题」最长 k 可重区间集 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据   ...

随机推荐

  1. 【Python】神器:Streamlit,仅使用Python开发一个运维管理后台(不需要编写html,js,css)

    背景 作为SRE,我们有很多很多自动化的工具,大部分都是自动运行的,还有一部分是CLI,我们一直苦于没有一个自己的管理后台网站,受限于前端能力薄弱,开发出来的网页只能说凑活能用,但是不好用. 现在我们 ...

  2. Nextcloud 使用教程

    一.简介 Nextcloud是一个网盘式文件管理系统,多用户权限管理,多客户端,使用简单.可在浏览器中运行,也可下客户端,不论使用哪种方式运行,使用教程都是一样的. 只是在客户端中运行时能及时收到相应 ...

  3. 友盟umeng消息推送直接复制就能用(纯干货)

    一. 单播推送(unicast) 1.1 图 1.2 代码 1 /** 2 * 根据设备的deviceToken, 去给指定的设备推送消息 3 * 4 * @param deviceToken 单个d ...

  4. Caffe框架GPU与MLU计算结果不一致请问如何调试?

    Caffe框架GPU与MLU计算结果不一致请问如何调试? 某一检测模型移植到Cambricon Caffe上时,发现无法检测出结果,于是将GPU和MLU的运行结果输出并保存后进行对比,发现二者计算结果 ...

  5. [NOIP1998 提高组] 拼数

    题目描述 设有 n 个正整数​ a1-an,将它们联接成一排,相邻数字首尾相接,组成一个最大的整数. 输入格式 第一行有一个整数,表示数字个数 n. 第二行有 n 个整数,表示给出的 n 个整数 a_ ...

  6. Ucore lab1实验报告

    练习一 Makefile 1.1 OS镜像文件ucore.img 是如何一步步生成的? + cc kern/init/init.c + cc kern/libs/readline.c + cc ker ...

  7. 在python_request 中 nb-log 日志模块的使用,应用到项目实际使用

    一.安装 pip install nb-log pycham 中安装: 二.基本使用 2.1 pycham中调整设置控制台日志打印出的颜色 2.2 设置完成后去掉console弹出的颜色设置 2.3  ...

  8. 【NX二次开发】图标图像

    用户定义位图的目录位置的环境变量 UGII_BITMAP_PATH 在NX日志中查看NX图标需要设置的变量 变量名:PRINT_DIALOG_BITMAP_NAMES 变量值:1 查看系统图标的方法1 ...

  9. 自动删除n天前的日志(此处用于业务删除xml文件)

    Linux应用总结:自动删除n天前的日志 linux是一个很能自动产生文件的系统,日志.邮件.备份等.虽然现在硬盘廉价,我们可以有很多硬盘空间供这些文件浪费,让系统定时清理一些不需要的文件很有一种爽快 ...

  10. DHCP的简单介绍与配置

    一.DHCP简介 二.DHCP报文类型 三.DHCP工作原理 四.实例操作 一.DHCP简介 DHCP(Dynamic Host Configuration Protocol),动态主机配置协议,是一 ...