「网络流 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;
}
View Code

 

于是就有了第二种方法,不需要把区间串起来,

通过对上面的解析,只有当两端区间相交时,并且需要的只是费用

很显然,假设我们不需要把整个区间建出来,只要处理出相交关系就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;
}
View Code

以上是关于「网络流 24 题」最长 k 可重区间集的主要内容,如果未能解决你的问题,请参考以下文章

网络流24题21最长k可重区间集问题

「网络流24题」「LuoguP3358」 最长k可重区间集问题

最长k可重区间集问题网络流24题

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

网络流24题No.21 (最长 k 可重区间集问题 最长不相交路径 最大费用流)

网络流24题22最长k可重线段集问题