POJ 3680

来源:互联网 发布:java 整形转string 编辑:程序博客网 时间:2024/06/06 04:24

建图很巧妙

先离散化区间端点。从0->1,1->2,~~~~~n->n+1每条边容量为k,费用为0,

对于每条线段,他的两个端点连边容量为1,费用-w

然后跑一遍最小费用流

算法正确性证明:

如果两个区间没有交集,那么代表它们的边可以出现在同一增广路上,这一点显然。否则,它们就在不同的增广路上。每一个区间对应的边的容量都是1,这样,最后的流量就是被选出的两两有交集的区间的数量。受到(0,1,k,0)这条边的容量限制,这个值刚好不大于k.区间的权都是正的,因此选取的区间多多益善,所以流量必然最大。

 (对于每次选取的增广路中总存在一个区间,在每次增广所得区间都与这个区间有交集)

#include<cstdio>#include<stdlib.h>#include<cstring>using namespace std;const int inf=99999999;struct{    int v, cap, cost, next, re;}edge[2005];struct{int a,b,w;}line[210];struct Li{int be,num;}li[450];int n,ans;int k,edgeHead[500];int que[500],pre[500],dis[500];bool vis[500];int cmp(const void * a,const void *b){      struct Li *aa=(struct Li *) a;      struct Li *bb=(struct Li *) b;  return aa->be-bb->be;  }  void addEdge(int u,int v,int ca,int co){    edge[k].v=v;    edge[k].cap=ca;    edge[k].cost=co;    edge[k].next=edgeHead[u];    edge[k].re=k + 1;               //这个用来记录此边的反边    edgeHead[u]=k ++;    edge[k].v=u;    edge[k].cap=0;    edge[k].cost=-co;               //故这里去反费用(因为是反向边)    edge[k].next=edgeHead[v];    edge[k].re=k - 1;    edgeHead[v]=k ++;}bool spfa(){                //寻找最长增广路时如果是正向边则+相应的费用,反向边-相应的费用    int i, head = 0, tail = 1;    //  长注释的地方就是从最小费用改到最大费用时需要变动的地方    for(i = 0; i <= n; i ++){        dis[i] = inf;////////////        vis[i] = false;    }    dis[0] = 0;    que[0] = 0;    vis[0] = true;    while(head != tail){        int u = que[head];        for(i = edgeHead[u]; i != 0; i = edge[i].next){            int v = edge[i].v;            if(edge[i].cap && dis[v] >dis[u] + edge[i].cost){////////                dis[v] = dis[u] + edge[i].cost;                pre[v] = i;                        // 这个pre数组记录的是从边号为i的那条边去往v                if(!vis[v]){                    vis[v] = true;                    que[tail ++] = v;                    if(tail == 490) tail = 0;             // 这里用到了循环队列,节省空间                }            }        }        vis[u] = false;        head++;        if(head ==490) head = 0;    }    if(dis[n] ==inf) return false;///////////    return true;}void end(){    int u, p,mi;mi=inf;for(u = n; u != 0; u = edge[edge[p].re].v){        p = pre[u];        if(edge[p].cap<mi)            mi=edge[p].cap;    }    for(u = n; u != 0; u = edge[edge[p].re].v){        p = pre[u];        edge[p].cap -= mi;        edge[edge[p].re].cap += mi;        ans += edge[p].cost*mi;    }}int main(){int i,j,T,t,v,lim,a,b,w;scanf("%d",&T);for(t=1;t<=T;t++){    k=1;memset(edgeHead,0,sizeof(edgeHead));scanf("%d %d",&v,&lim);for(i=1;i<=v;i++){scanf("%d %d %d",&a,&b,&w);li[i*2-1].be=a;li[i*2-1].num=-i;li[i*2].be=b;li[i*2].num=i;line[i].a=a;line[i].b=b;line[i].w=w;}qsort(&li[1],2*v,sizeof(li[1]),cmp);int temp=li[1].be,tp=1;for(i=1;i<=v*2;i++){if(li[i].be!=temp){tp++;temp=li[i].be;}if(li[i].num>0) //注意这里不能写成if(li[i].num)因为大于0和小于0都满足........在这里T了无数次line[li[i].num].b=tp;elseline[-li[i].num].a=tp;}for(i=0;i<=tp;i++){addEdge(i,i+1,lim,0);}n=tp+1;for(i=1;i<=v;i++){addEdge(line[i].a,line[i].b,1,-line[i].w);}    ans=0;while(spfa())end();printf("%d\n",-ans);}    return 0;}


 

用STL更简捷

#include<cstdio>#include<algorithm>#include<cstring>using namespace std;const int inf=99999999;struct{    int v, cap, cost, next, re;}edge[2005];struct{int a,b,w;}line[210];int li[450];int n,ans;int k,edgeHead[500];int que[500],pre[500],dis[500];bool vis[500];void addEdge(int u,int v,int ca,int co){    edge[k].v=v;    edge[k].cap=ca;    edge[k].cost=co;    edge[k].next=edgeHead[u];    edge[k].re=k + 1;               //这个用来记录此边的反边    edgeHead[u]=k ++;    edge[k].v=u;    edge[k].cap=0;    edge[k].cost=-co;               //故这里去反费用(因为是反向边)    edge[k].next=edgeHead[v];    edge[k].re=k - 1;    edgeHead[v]=k ++;}bool spfa(){                //寻找最长增广路时如果是正向边则+相应的费用,反向边-相应的费用    int i, head = 0, tail = 1;    //  长注释的地方就是从最小费用改到最大费用时需要变动的地方    for(i = 0; i <= n; i ++){        dis[i] = inf;////////////        vis[i] = false;    }    dis[0] = 0;    que[0] = 0;    vis[0] = true;    while(head != tail){        int u = que[head];        for(i = edgeHead[u]; i != 0; i = edge[i].next){            int v = edge[i].v;            if(edge[i].cap && dis[v] >dis[u] + edge[i].cost){////////                dis[v] = dis[u] + edge[i].cost;                pre[v] = i;                        // 这个pre数组记录的是从边号为i的那条边去往v                if(!vis[v]){                    vis[v] = true;                    que[tail ++] = v;                    if(tail == 490) tail = 0;             // 这里用到了循环队列,节省空间                }            }        }        vis[u] = false;        head++;        if(head ==490) head = 0;    }    if(dis[n] ==inf) return false;///////////    return true;}void end(){    int u, p,mi;mi=inf;for(u = n; u != 0; u = edge[edge[p].re].v){        p = pre[u];        if(edge[p].cap<mi)            mi=edge[p].cap;    }    for(u = n; u != 0; u = edge[edge[p].re].v){        p = pre[u];        edge[p].cap -= mi;        edge[edge[p].re].cap += mi;        ans += edge[p].cost*mi;    }}int main(){int i,j,T,t,v,lim,a,b,w;int cnt;scanf("%d",&T);for(t=1;t<=T;t++){    k=1;    cnt=0;memset(edgeHead,0,sizeof(edgeHead));scanf("%d %d",&v,&lim);for(i=1;i<=v;i++){scanf("%d %d %d",&a,&b,&w);li[cnt++]=a;li[cnt++]=b;line[i].a=a;line[i].b=b;line[i].w=w;}sort(li,li+cnt);n=unique(li,li+cnt)-li;for(i=1;i<=n;i++)            addEdge(i-1,i,lim,0);for(i=1;i<=v;i++){    int x=lower_bound(li,li+n,line[i].a)-li+1;    int y=lower_bound(li,li+n,line[i].b)-li+1;addEdge(x,y,1,-line[i].w);}    ans=0;while(spfa())end();printf("%d\n",-ans);}    return 0;}



 

原创粉丝点击