poj/pku 3680(最小费用最大流)

题目链接:http://poj.org/problem?id=3680


题意描述:给你n(n<=200)个区间,每个区间有一个权值,且每个区间的实数点最多只能访问k次,现在让你选择区间使得所选权值和最大


分析:该题是最大费用最大流,k可以看成是容量限制,刚开始这样想,将给出的每个区间看成一个点,然后将区间(i,i+1)i从0一直取到最大看成一些点,那么源点连接每个给出区间点,自己创造的区间点连接汇点,每个区间点都对应一些自己创造的区间点,但是这样做,不能正确的确定边的容量和费用,所以这样是解不出来的,无赖只好百度了一下,网上的建图方法经典至极啊,是将区间看成一条边而我只停留在将区间看成点的程度上,将区间看成边,那么对于这样的区间(i,i+1)那么他们最多允许流过k次,费用设为0,那么对给出区间的区间,这样的边就可以流过的流量设为1,表示只能选择一次,费用自然是区间的权值,现在这里给出建图的方法:建图之前可以将区间点离散化一下,这样可以减少些点,使得点最多不超过402个,那么我们建立源点和汇点,源点连接第一个点容量为k,费用为0,最后一个点连接汇点费用为0,容量为k,之间相邻的点相连,费用为0,容量为k,此处建图就体现了区间中所有的点最多只能流过k次,即最多只能被覆盖k次,之后对于给出的区间,我们连接区间的两个端点,容量为1,如果求最小费用则费用这里取反,否则为正!假设我们求最大费用最大流,我们每次都找源点到汇点的费用最长路,且每次找出一条路径都会将其经过的区间覆盖一次,如果不存在增广路则意味着不能在继续选区间啦!


代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=410;
const int E=20000;
const int inf=0x3fffffff;
struct node
{
    int x, y,nxt,c,w;
}edge[E];
int head[N],e;
int src,sin;
struct node1
{
    int x, y,c;
}a[N/2];
int b[N];
void addedge(int x,int y, int w, int c)
{
    edge[e].x=x;
    edge[e].y=y;
    edge[e].w=w;
    edge[e].c=c;
    edge[e].nxt=head[x];
    head[x]=e++;

    edge[e].x=y;
    edge[e].y=x;
    edge[e].w=0;
    edge[e].c=-c;
    edge[e].nxt=head[y];
    head[y]=e++;

}
int find(int x,int high)
{
    int low=1;
    while(low<=high)
    {
        int mid=(low+high)>>1;
        if(b[mid]==x)
        return mid;
        if(b[mid]<x)
        low=mid+1;
        else high=mid-1;
    }
}

int dis[N],que[N],pre[N];
int Cost,Flow;
bool isque[N];
void mincmaxf()
{
    Cost=0;Flow=0;int u,v,i;
    while(1)
    {
        int f=0,r=1;
        for(i=src;i<=sin;i++)
        {
            dis[i]=inf;
            isque[i]=false;
        }
        dis[src]=0;
        que[0]=src;
        isque[src]=true;
        while(f!=r)
        {
            u=que[f];
            f=(f+1)%N;
            for(i=head[u];i!=-1;i=edge[i].nxt)
            {
                 v=edge[i].y;
                if(dis[v]>dis[u]+edge[i].c&&edge[i].w)
                {
                    dis[v]=dis[u]+edge[i].c;
                    pre[v]=i;
                    if(!isque[v])
                    {
                        que[r]=v;
                        r=(r+1)%N;
                        isque[v]=true;
                    }
                }
            }
            isque[u]=false;
        }
        if(dis[sin]==inf)break;
        int p,min=inf;
        for(u=sin;u!=src;u=edge[pre[u]].x)
        {
            p=pre[u];
            if(min>edge[p].w)
            min=edge[p].w;
        }
        Flow+=min;
        Cost+=min*dis[sin];
        for(u=sin;u!=src;u=edge[pre[u]].x)
        {
            p=pre[u];
            edge[p].w-=min;
            edge[p^1].w+=min;
        }
    }
}
int main ()
{
    int t,n,k,i,j,x,y,c;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        memset(head,-1,sizeof(head));
        e=0;
        for(i=1;i<=n;i++)
        {
            scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].c);
            b[i*2-1]=a[i].x;
            b[i*2]=a[i].y;
        }
        sort(b+1,b+2*n+1);
        for(i=2,j=2;i<=2*n;i++)
        if(b[i]!=b[i-1])
        b[j++]=b[i];
        sin=j;src=0;
        for(i=0;i<j;i++)
        addedge(i,i+1,k,0);
        for(i=1;i<=n;i++)
        {
            x=find(a[i].x,j-1);
            y=find(a[i].y,j-1);
            if(x>y)swap(x,y);
            addedge(x,y,1,-a[i].c);
        }
        mincmaxf();
        printf("%d\n",-Cost);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值