「网络流 24 题」最长 k 可重区间集
Posted shatianming
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「网络流 24 题」最长 k 可重区间集相关的知识,希望对你有一定的参考价值。
挺有意思的一道题,嘛,还是那句话,不要被固有思维给限制了
嘛,我一开始找点来逐步分析,而后才看了题解发现时找边的关系,我很容易找题目不关紧要的条件啊.....
首先这个题有两种建图方法
第一种,直接把minl----maxl串起来,流量无穷大,费用为0,然后对于一个区间,Li,Ri
从Li---Ri连一条边,流量1,费用Ri-Li,
你建完图会发现,只有当两端区间相交时,才互相影响,所以,很好的处理除了区间只有k个....
但....
嘛,很不理想,
那我们可以再在上面改进一下
#include<bits/stdc++.h> using namespace std; int k,n,tot=-1,h[40005],inf=999999,maxl=0,minl=999999; struct node{ int from,to,cost,next,rest; }e[1000005]; int dis[40005],vis[40005],flow[40005],g[40005],ans=0,ans2=0; void bfs(int s,int t){ memset(dis,0x7f,sizeof(dis)); memset(vis,false,sizeof(vis)); memset(flow,0x7f,sizeof(flow)); memset(g,-1,sizeof(g)); queue<int>q;q.push(s);vis[s]=true;dis[s]=0; while(!q.empty()){ int u=q.front();q.pop();vis[u]=false; for(int i=h[u];i!=(-1);i=e[i].next){ if(e[i].rest>0&&dis[e[i].to]>dis[u]+e[i].cost){ dis[e[i].to]=dis[u]+e[i].cost; g[e[i].to]=i; flow[e[i].to]=min(e[i].rest,flow[u]); if(vis[e[i].to]==false){ vis[e[i].to]=true; q.push(e[i].to); } } } } } int EK(int s,int t){ while(1){ bfs(s,t); if(g[t]==(-1))break; ans+=flow[t],ans2+=flow[t]*dis[t]; for(int p=t;p!=s;p=e[g[p]].from){ e[g[p]].rest-=flow[t]; e[g[p]^1].rest+=flow[t]; } } } void add(int x,int y,int z,int hg){ tot++; e[tot].next=h[x]; h[x]=tot; e[tot].cost=hg; e[tot].from=x; e[tot].to=y; e[tot].rest=z; } void adde(int x,int y,int z,int hg){ add(x,y,z,hg); add(y,x,0,-hg); } void init(){ tot=(-1); ans=0,ans2=0; memset(h,-1,sizeof(h)); } struct node2{ int l,r; }ok[1005]; bool cmp(node2 a,node2 b){ return a.l<b.r; } int main(){ init(); cin>>n>>k; for(int i=1;i<=n;i++){ cin>>ok[i].l>>ok[i].r; maxl=max(maxl,ok[i].r); minl=min(minl,ok[i].l); } sort(ok+1,ok+1+n,cmp); for(int i=1;i<=n;i++){ adde(ok[i].l,ok[i].r,1,-(ok[i].r-ok[i].l)); } for(int i=minl;i<maxl;i++){ adde(i,i+1,inf,0); } adde(0,minl,k,0); adde(maxl,20000,k,0); EK(0,20000); cout<<-ans2<<endl; }
于是就有了第二种方法,不需要把区间串起来,
通过对上面的解析,只有当两端区间相交时,并且需要的只是费用
很显然,假设我们不需要把整个区间建出来,只要处理出相交关系就ok了
于是就有了步骤
先将区间排序(按左端点)
将每一个区间拆成两个点
当有区间与这个区间不相交时,
将上端点与那个端点下端点连起来
同样很好的处理了区间内选k个的这种操作
正确性显然把,假设后面有出现新的可以分配的区间,那么前面跟他不想交的直接连上...
#include<bits/stdc++.h> using namespace std; int k,n,tot=-1,h[40005],inf=999999; struct node{ int from,to,cost,next,rest; }e[1000005]; int dis[40005],vis[40005],flow[40005],g[40005],ans=0,ans2=0; void bfs(int s,int t){ memset(dis,0x7f,sizeof(dis)); memset(vis,false,sizeof(vis)); memset(flow,0x7f,sizeof(flow)); memset(g,-1,sizeof(g)); queue<int>q;q.push(s);vis[s]=true;dis[s]=0; while(!q.empty()){ int u=q.front();q.pop();vis[u]=false; for(int i=h[u];i!=(-1);i=e[i].next){ if(e[i].rest>0&&dis[e[i].to]>dis[u]+e[i].cost){ dis[e[i].to]=dis[u]+e[i].cost; g[e[i].to]=i; flow[e[i].to]=min(e[i].rest,flow[u]); if(vis[e[i].to]==false){ vis[e[i].to]=true; q.push(e[i].to); } } } } } int EK(int s,int t){ while(1){ bfs(s,t); if(g[t]==(-1))break; ans+=flow[t],ans2+=flow[t]*dis[t]; for(int p=t;p!=s;p=e[g[p]].from){ e[g[p]].rest-=flow[t]; e[g[p]^1].rest+=flow[t]; } } } void add(int x,int y,int z,int hg){ tot++; e[tot].next=h[x]; h[x]=tot; e[tot].cost=hg; e[tot].from=x; e[tot].to=y; e[tot].rest=z; } void adde(int x,int y,int z,int hg){ add(x,y,z,hg); add(y,x,0,-hg); } void init(){ tot=(-1); ans=0,ans2=0; memset(h,-1,sizeof(h)); } struct node2{ int l,r; }ok[1005]; bool cmp(node2 a,node2 b){ return a.l<b.l; } int main(){ init(); cin>>n>>k; for(int i=1;i<=n;i++){ cin>>ok[i].l>>ok[i].r; } sort(ok+1,ok+1+n,cmp); adde(0,5000,k,0); for(int i=1;i<=n;i++){ adde(5000,i,1,0); adde(i,i+n,1,-(ok[i].r-ok[i].l)); adde(i+n,9999,1,0); for(int j=i+1;j<=n;j++){ if(ok[i].r<=ok[j].l){ adde(i+n,j,1,0); } } } EK(0,9999); cout<<-ans2<<endl; }
以上是关于「网络流 24 题」最长 k 可重区间集的主要内容,如果未能解决你的问题,请参考以下文章
「网络流24题」「LuoguP3358」 最长k可重区间集问题