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

« 问题描述:
给定实直线 L n 个开区间组成的集合 I ,和一个正整数 k ,试设计一个算法,从开区间集合 I 中选取出开区间集合 S Í I ,使得在实直线 L 的任何一点 x S 中包含点 x 的开区间
个数不超过
k ,且sigma(|z|) 达到最大。这样的集合 S 称为开区间集合 I 的最长 k 可重区间集。

sigma(|z|)称为最长k 可重区间集的长度。

« 编程任务:
对于给定的开区间集合 I 和正整数 k ,计算开区间集合 I 的最长 k 可重区间集的长度。
« 数据输入:
由文件 input.txt 提供输入数据。文件的第 1 行有 2 个正整数 n k ,分别表示开区间的个数和开区间的可重迭数。接下来的 n 行,每行有 2 个整数,表示开区间的左右端点坐标。
« 结果输出 :

程序运行结束时,将计算出的最长 k 可重区间集的长度输出到文件 output.txt中。

输入文件示例
input.txt
4 2
1 7
6 8
7 10
9 13

输出文件示例
output.txt
15
题解:这道题属于网路流中的最大权不相交路径问题
先离散化,把原来的n的线段的端点离散化成1~2n这些数(这步是为了后面的处理更加方便)
建图方式:
1、超级源s到1号点(也就是最左边线段的左端点)连一条流量为k 费用为0的有向边
2、2n号点到超级汇t(也就是最右边线段的右端点)连一条流量为k 费用为0的有向边
3、除s、t外的所有点x到x+1连一条流量为无限大 费用为0的有向边
4、原来的所有线段的左端点到右端点连一条流量为1 费用为线段长度(在这题中=右端点-左端点,但因为是开区间,所以两头都不能要啊,应该=右端点-左端点-1)的有向边
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <math.h>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#include <limits.h>
#include <malloc.h>
#include <ctype.h>
#include <float.h>
using namespace std;

const int oo=1000000005;
const int Maxn=100005;
const int Maxm=100005;

int i,ans,n,k,s,t,len,x;
int head[Maxn];
int lz[Maxn];
int l[Maxn];
int dis[Maxn];
int a[Maxn];
int vis[Maxn];
int pre[Maxn];
int rz[Maxn];
int r[Maxn];

struct node{
    int num,z;//num是数值 z是在原数组中的位置 l数组为i r数组为n+i
    node(){
        num=z=0;
    }
    node(int x,int y){
        num=x;z=y;
    }
    bool operator < (const node &s) const{
        return num<s.num;
    }
} lsh[Maxn<<1];

struct edge{
    int u,v,c,f,w,next;
    edge(){
        u=v=c=f=w=0;
        next=-1;
    }
    edge(int x,int y,int z,int a,int s,int d){
        u=x;v=y;c=z;f=a;w=s;next=d;
    }
} e[Maxm];   

void add(int u,int v,int c,int f,int w){
    len++;
    e[len]=edge(u,v,c,f,w,head[u]);
    head[u]=len;
}

int spfa(int s,int t,int m){
    int x;
    int sum=0;
    queue <int> q;
    for(i=1;i<=m;i++){
        vis[i]=0;
        dis[i]=oo;
        a[i]=oo;
    }
    q.push(s);
    vis[s]=1;
    dis[s]=0;
    a[s]=oo;
    while(!q.empty()){
        x=q.front();
        for(i=head[x];i!=-1;i=e[i].next)
            if(dis[x]+e[i].w<dis[e[i].v]&&e[i].c>e[i].f){
                dis[e[i].v]=dis[x]+e[i].w;
                pre[e[i].v]=i;
                a[e[i].v]=min(e[i].c-e[i].f,a[x]);
                if(!vis[e[i].v]){
                    q.push(e[i].v);
                    vis[e[i].v]=1;
                }
            }
        q.pop();
        vis[x]=0;
    }
    if(dis[t]==oo)
        return -1;
    x=t;
    while(x!=s){
        e[pre[x]].f+=a[t];
        e[pre[x]^1].f-=a[t];
        sum+=a[t]*e[pre[x]].w;
        x=e[pre[x]].u;
        //printf("sum=%d\n",sum);
    }
    return sum;
}

int main(){
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
    scanf("%d %d",&n,&k);
    for(i=1;i<=2*n+2;i++)
        head[i]=-1;
    len=-1;
    for(i=1;i<=n;i++){
        scanf("%d %d",&l[i],&r[i]);
        lsh[2*i-1]=node(l[i],i);
        lsh[2*i]=node(r[i],n+i);
    }
    sort(lsh+1,lsh+1+(n<<1));
    for(i=1;i<=(n<<1);i++){
        if(lsh[i].z<=n)
            lz[lsh[i].z]=i;
        else
            rz[lsh[i].z-n]=i;
    }
    s=(n<<1)+1;
    t=(n<<1)+2;
    add(s,1,k,0,0);
    add(1,s,0,0,0);
    add(n*2,t,k,0,0);
    add(t,n*2,0,0,0);
    for(i=1;i<(n<<1);i++){
        add(i,i+1,oo,0,0);
        add(i+1,i,0,0,0);
    }
    for(i=1;i<=n;i++){
        add(lz[i],rz[i],1,0,-(r[i]-l[i]));
        add(rz[i],lz[i],0,0,r[i]-l[i]);
    }
    //for(i=0;i<=len;i++)
    //    printf("%d:%d %d %d %d %d\n",i,e[i].u,e[i].v,e[i].c,e[i].f,e[i].w);
    while(1){
        x=spfa(s,t,n*2+2);
        if(x==-1)
            break;
        ans+=x;
        //printf("%d\n",-ans);
    }
    printf("%d\n",-ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值