【刷题】LOJ 6014 「网络流 24 题」最长 k 可重区间集

题目描述

给定实直线 \(L\) 上 \(n\) 个开区间组成的集合 \(I\) ,和一个正整数 \(k\) ,试设计一个算法,从开区间集合 \(I\) 中选取出开区间集合 \(S \subseteq I\) ,使得在实直线 \(L\) 的任何一点 \(x\) ,\(S\) 中包含点 \(x\) 的开区间个数不超过 \(k\) 。且 \(\sum\limits_{z \in S} | z |\) 达到最大。这样的集合 \(S\) 称为开区间集合 \(I\) 的最长 \(k\) 可重区间集。\(\sum\limits_{z \in S} | z |\) 称为最长 \(k\) 可重区间集的长度。

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

输入格式

文件的第 \(1\) 行有 \(2\) 个正整数 \(n\) 和 \(k\) ,分别表示开区间的个数和开区间的可重迭数。

接下来的 \(n\) 行,每行有 \(2\) 个整数 \(l_i\) 和 \(r_i\) ,表示开区间的左右端点坐标,注意可能有 \(l_i > r_i\) ,此时请将其交换

输出格式

输出最长 \(k\) 可重区间集的长度。

样例

样例输入

4 2
1 7
6 8
7 10
9 13

样例输出

15

数据范围与提示

\(1 \leq n \leq 500, 1 \leq k \leq 3\)

题解

先离散化

然后每个点向后面一个点连容量为 \(inf\) ,费用为 \(0\) 的边

对于一个区间 \(l,r\) ,从 \(l\) 连向 \(r\) ,容量为 \(1\) ,费用为其长度的相反数,代表一个区间只能选一次,选一次的贡献为它的长度

这样建模跑费用流就可以使答案最大

但是还有每个点只能被覆盖 \(k\) 的限制

那么源点向 \(1\) 号点连容量为 \(k\) ,费用为 \(0\) 的边

\(n\) 号点向汇点连容量为 \(k\) ,费用为 \(0\) 的边

在一次增广中,每个点都只会被经过一次

那么最大流一定为 \(k\) ,即 \(k\) 次增广,所以每个点只会被经过 \(k\) 次,满足题目限制

#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=1000+10,MAXM=(MAXN<<1),inf=0x3f3f3f3f;
int n,k,e=1,beg[MAXN],cur[MAXN],L[MAXN],R[MAXN],r,level[MAXN],p[MAXN],vis[MAXN],clk,s,t,nex[MAXM<<1],to[MAXM<<1],cap[MAXM<<1],was[MAXM<<1],val[MAXN];
ll answas;
std::queue<int> q;
std::vector<int> V;
std::map<int,int> M;
template<typename T> inline void read(T &x)
{
T data=0,w=1;
char ch=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
x=data*w;
}
template<typename T> inline void write(T x,char ch='\0')
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
if(ch!='\0')putchar(ch);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline void insert(int x,int y,int z,int w)
{
to[++e]=y;
nex[e]=beg[x];
beg[x]=e;
cap[e]=z;
was[e]=w;
to[++e]=x;
nex[e]=beg[y];
beg[y]=e;
cap[e]=0;
was[e]=-w;
}
inline void discretization()
{
for(register int i=1;i<=n;++i)V.push_back(L[i]),V.push_back(R[i]);
std::sort(V.begin(),V.end());
V.erase(std::unique(V.begin(),V.end()),V.end());
for(register int i=0,lt=V.size();i<lt;++i)M[V[i]]=i+1;
for(register int i=1;i<=n;++i)L[i]=M[L[i]],R[i]=M[R[i]],chkmax(r,R[i]);
}
inline bool bfs()
{
memset(level,inf,sizeof(level));
level[s]=0;
p[s]=1;
q.push(s);
while(!q.empty())
{
int x=q.front();
q.pop();
p[x]=0;
for(register int i=beg[x];i;i=nex[i])
if(cap[i]&&level[to[i]]>level[x]+was[i])
{
level[to[i]]=level[x]+was[i];
if(!p[to[i]])p[to[i]]=1,q.push(to[i]);
}
}
return level[t]!=inf;
}
inline int dfs(int x,int maxflow)
{
if(x==t||!maxflow)return maxflow;
vis[x]=clk;
int res=0;
for(register int &i=cur[x];i;i=nex[i])
if((vis[to[i]]^vis[x])&&cap[i]&&level[to[i]]==level[x]+was[i])
{
int f=dfs(to[i],min(maxflow,cap[i]));
res+=f;
cap[i]-=f;
cap[i^1]+=f;
maxflow-=f;
answas+=1ll*was[i]*f;
if(!maxflow)break;
}
vis[x]=0;
return res;
}
inline void MCMF()
{
while(bfs())clk++,memcpy(cur,beg,sizeof(cur)),dfs(s,inf);
}
int main()
{
read(n);read(k);
for(register int i=1;i<=n;++i)
{
read(L[i]);read(R[i]);
if(L[i]>R[i])std::swap(L[i],R[i]);
val[i]=R[i]-L[i];
}
discretization();
s=r+1,t=s+1;
insert(s,1,k,0);insert(r,t,k,0);
for(register int i=1;i<r;++i)insert(i,i+1,inf,0);
for(register int i=1;i<=n;++i)insert(L[i],R[i],1,-val[i]);
MCMF();
write(-answas,'\n');
return 0;
}
上一篇:linux 启动管理


下一篇:【MySQL 读书笔记】当我们在使用索引的时候我们在做什么