「网络流 24 题」[15] 最长 k 可重区间集

题意

给定一个区间集s,含n个开区间,对于这个区间集,定义最长k可重区间集为s的一个子集s',s'满足:

对于数轴上任意一个点,最多被区间s'内的区间覆盖k次。

定义对于一个区间集,它的长度为子集内所有区间的长度总和。

最长k可重区间集为所有s'里最长的长度。

题目

分析

简单来说就是考虑找k个不重叠的区间集使得长度最长,每个区间最多被取一次。

第一步就没想到

然后来考虑这个东西怎么寻找。

可以简单的发现,如果要不重叠,那么选定一段区间之后这个区间覆盖的所有区间都不能取,但是之后的区间又都可以取。

然后就会变成类似DP一样的东西。但是这个时候发现写不动啊。因为每一个区间最多被使用一次。

考虑到这个限制,来想一想网络流吧。

然后建图也很明朗了...

首先离散。假设最后剩下来的点一共有tot个。

建立源点s和汇点t,s连一条到1节点流量为k费用为0的边,tot连一条到t节点流量为k(其实大于等于k都行)费用为0的边。

然后对于每个点i,如果i+1存在的话,那么连一条i到i+1流量为inf(其实大于等于k都行)费用为0的边。

然后对于区间集内任意一对(l,r)连一条离散后,l到r流量为1(只能使用一次),费用为len(l,r)的边。

然后跑一次最大费用最大流就好了。

code


#include<bits/stdc++.h>
#define M 1005
#define inf 1000000000
using namespace std;
void read(int &x){
	x=0; char c=getchar();
	for (;c<48;c=getchar());
	for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
struct ed{
	int x,cap,cost,nx;
}e[M*20];
int nx[M],ecnt;
void add(int x,int y,int cap,int cost){
//	printf("%d %d   %d %d\n",x,y,cap,cost);
	e[ecnt]=(ed){y,cap,cost,nx[x]};
	nx[x]=ecnt++;
	e[ecnt]=(ed){x,0,-cost,nx[y]};
	nx[y]=ecnt++;
}
struct EK_EK{
	int Q[M],dis[M],nxt[M],s,t,l,r,Flow,Cost;
	bool vis[M];
	bool spfa(int x){
	//	printf("s=%d t=%d\n",s,t);
		for (int i=s;i<=t;i++)dis[i]=-inf;
		dis[x]=0;
		l=r=0;
		Q[r++]=x;
		for (;l!=r;){
			x=Q[l++]; l%=M;
			vis[x]=0;
			for (int i=nx[x];~i;i=e[i].nx)if (e[i].cap>0&&e[i].cost+dis[x]>dis[e[i].x]){
				dis[e[i].x]=dis[x]+e[i].cost; nxt[e[i].x]=i;
				if (!vis[e[i].x]){
					Q[r++]=e[i].x; vis[e[i].x]=1; r%=M;
				}
			}
		}
		return dis[t]>-inf;
	}

	int dfs(int x,int f){
		if (x==s){
			Cost+=f*dis[t];
			return f;
		}
		int res=dfs(e[nxt[x]^1].x,min(f,e[nxt[x]].cap));
		e[nxt[x]].cap-=res; e[nxt[x]^1].cap+=res;
		return res;
	}
	void solve(int ss,int tt){
		s=ss; t=tt;
		for (;spfa(s);){
			Flow+=dfs(t,inf);
		}
	}
}EK;

int a[M],l[M],r[M];
int ch(int l,int r,int x){
	for (;;){
		int mid=(l+r)>>1;
		if (a[mid]==x)return mid;
		if (a[mid]<x)l=mid+1;else r=mid-1;
	}
}

int main(){
//	freopen("LOJ6014.in","r",stdin);
	int n,k;
	read(n); read(k);
	for (int i=1;i<=n;i++){
		read(l[i]); read(r[i]);
		if (l[i]>r[i])swap(l[i],r[i]);
		a[i]=l[i]; a[i+n]=r[i];
	}
	sort(a+1,a+n+n+1);
	memset(nx,-1,sizeof(nx));
	int tot=unique(a+1,a+n+n+1)-a-1;
	int s,t;
	s=0; t=tot+1;
	add(s,1,k,0); //add(tot,t,k,0);
	for (int i=1;i<=tot;i++)add(i,i+1,inf,0);
	for (int i=1;i<=n;i++){
		int len=r[i]-l[i];
		l[i]=ch(1,tot,l[i]); r[i]=ch(1,tot,r[i]);
	//	printf("%d %d\n",l[i],r[i]);
		add(l[i],r[i],1,len);
	}
	EK.solve(s,t);
	printf("%d\n",EK.Cost);
	return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值