码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2020-01-22 21:33:21      阅读:86      评论:0      收藏:0      [点我收藏+]

标签:题目   name   view   play   front   next   流量   while   rest   

挺有意思的一道题,嘛,还是那句话,不要被固有思维给限制了

嘛,我一开始找点来逐步分析,而后才看了题解发现时找边的关系,我很容易找题目不关紧要的条件啊.....

首先这个题有两种建图方法

第一种,直接把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 可重区间集

标签:题目   name   view   play   front   next   流量   while   rest   

原文地址:https://www.cnblogs.com/shatianming/p/12229590.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!