这个问题想通了也确实不难

问题:把区间看成一个点不好处理点权,所以要转化为边权

首先把区间拆成左端点和右端点,左右端点的边流量1,费用区间长度

这个应该不难理解,每个区间只能选一次嘛,价值是长度



问题:每个点不能有超过k个区间覆盖

其实就是我们同一时间的流量不能大于k

就是说,从源点到汇点流的过程中,任意时刻不能同时在k条路径上走

否则就是同一个区域内选了k个区间,这是不满足的

我选了某个区间,直到这个区间的右端点,我已经把流量分出1了

这个时候流量还剩k-1,在达到那个右端点之前,我最多能选k-1个区间

比如对于(1,5),(8,9),(2,10)来说,图大概这个样子


网络流24题( 最长k可重区间集问题)_离散化

假如从1流到5了,如果k=1,那么没有流量从1留到2之后了

假如从1留到5了,k=2,那么还有1的流量可以继续往后流到2

那么到2了因为还有流量1,所以可以选择从2流到10,也就是选择(2,10)

当然也可以从2流到5,之后再做选择。

具体连边

所有坐标离散化,左边的点连向右边的点,流量k,边权0

对于每个区间,左端点连向右端点,流量1,边权为区间长度

#include <bits/stdc++.h>
using namespace std;
const int inf=1e9;
const int maxn=2e5+10;
int n,k,li[maxn],ri[maxn],b[maxn],top,s,t,flow[maxn];
struct edge{
int to,nxt,flow,w;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int flow,int w){
d[++cnt]=(edge){v,head[u],flow,w},head[u]=cnt;
d[++cnt]=(edge){u,head[v],0,-w},head[v]=cnt;
}
int dis[maxn],vis[maxn],pre[maxn];
bool spfa()
{
queue<int>q;
for(int i=0;i<=t;i++) dis[i]=-inf,vis[i]=0;
q.push(s),dis[s]=0;
flow[s]=inf;
while( !q.empty() )
{
int u=q.front(); q.pop();
vis[u]=0;
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
if( dis[v]<dis[u]+d[i].w&&d[i].flow )
{
dis[v]=dis[u]+d[i].w;
if( !vis[v] ) vis[v]=1,q.push(v);
flow[v]=min( flow[u],d[i].flow );
pre[v]=i;
}
}
}
if( dis[t]==-inf ) return false;
return true;
}
int dinic()
{
int maxflow=0,mincost=0;
while( spfa() )
{
int x=t;
maxflow+=flow[t],mincost+=flow[t]*dis[t];
while( x!=s )
{
int i=pre[x];
d[i].flow-=flow[t];
d[i^1].flow+=flow[t];
x=d[i^1].to;
}
}
return mincost;
}
int main()
{
cin >> n >> k;
for(int i=1;i<=n;i++)
{
++top; cin >> b[top];
++top; cin >> b[top];
li[i]=b[top-1],ri[i]=b[top];
}
sort(b+1,b+1+top);
top=unique(b+1,b+1+top)-b-1;
s=0,t=top+1;
for(int i=1;i<=n;i++)
{
int len=ri[i]-li[i];
li[i]=lower_bound( b+1,b+1+top,li[i] )-b;
ri[i]=lower_bound( b+1,b+1+top,ri[i] )-b;
add( li[i],ri[i],1,len);
}
for(int i=0;i<t;i++) add(i,i+1,k,0);
cout << dinic();
}