POJ 3680 最小费用最大流

这题来源:《算法竞赛经典入门-训练指南》中的367页:区间k覆盖问题。

思路:这题建图比较机智,我刚开始想到能建的图也就是离散化后两个端点连边,流量为1,费用为负的权值(因为求的是最大费用最大流),然后再加上源点和汇点,也就如此而已;但是这样建图样例第二和第四个不正确,因为中间没有联系的没连边,然后k就没用了。

原来最重要的连边是i和i+1之间的连边,流量为k,费用为0;为什么要连这些边呢,刚开始我也没想明白,后面才知道,因为有的端点之间你要让它们产生联系并且受制与k次,那么就得把这些点都连边,这样就有联系了嘛!

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<bitset>
#define mem(a,b) memset(a,b,sizeof(a))
#define lson i<<1,l,mid
#define rson i<<1|1,mid+1,r
#define llson j<<1,l,mid
#define rrson j<<1|1,mid+1,r
#define INF 0x7fffffff
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
#define maxn 20005
struct
{
    int v,w,c,next,re;
    //re记录逆边的下标,c是费用,w是流量
} e[maxn];
int n,cnt;
int head[maxn],que[maxn*8],pre[maxn],dis[maxn];
bool vis[maxn];
void add(int u, int v, int w, int c)
{
    e[cnt].v=v,e[cnt].w=w,e[cnt].c=c;
    e[cnt].next=head[u];
    e[cnt].re=cnt+1,head[u]=cnt++;
    e[cnt].v=u,e[cnt].w=0,e[cnt].c=-c;
    e[cnt].next=head[v];
    e[cnt].re=cnt-1,head[v]=cnt++;
}
bool spfa()
{
    int i, l = 0, r = 1;
    for(i = 0; i <= n; i ++)
        dis[i] = INF,vis[i] = false;
    dis[0]=0,que[0]=0,vis[0]=true;
    while(l<r)
    {
        int u=que[l++];
        for(i=head[u];i!=-1;i=e[i].next)
        {
            int v = e[i].v;
            if(e[i].w&&dis[v]>dis[u]+e[i].c)
            {
                dis[v] = dis[u] + e[i].c;
                pre[v] = i;
                if(!vis[v])
                {
                    vis[v] = true;
                    que[r++] = v;
                }
            }
        }
        vis[u] = false;
    }
    return dis[n]!=INF;
}
int change()
{
    int i,p,sum=INF,ans=0;
    for(i=n;i!=0;i=e[e[p].re].v)
    {//e[e[p].re].v为前向结点,不理解看add和spfa
        p=pre[i];//p为前向结点编号
        sum=min(sum,e[p].w);
    }
    for(i=n;i!=0;i=e[e[p].re].v)
    {
        p=pre[i];
        e[p].w-=sum;
        e[e[p].re].w+=sum;
        ans+=sum*e[p].c;//c记录的为单位流量费用,必须得乘以流量。
    }
    return ans;
}
int EK()
{
    int sum=0;
    while(spfa()) sum+=change();
    return sum;
}
void init()
{
    mem(head,-1),mem(pre,0),cnt=0;
}
struct abc
{
    int u,v,w;
}aa[405];
int main()
{
    //freopen("1.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        init();
        int N,K,i,j,x,y,tmp=0,a[402];
        map<int,int>Map;
        scanf("%d%d",&N,&K);
        for(i=0;i<N;i++)
        {
            scanf("%d%d%d",&aa[i].u,&aa[i].v,&aa[i].w);
            a[tmp++]=aa[i].u,a[tmp++]=aa[i].v;
        }
        sort(a,a+tmp);
        int k=unique(a,a+tmp)-a;
        for(i=0,tmp=0;i<k;i++)
            Map[a[i]]=++tmp;
        for(i=0;i<N;i++)
        {
            int u=Map[aa[i].u],v=Map[aa[i].v];
            add(u,v,1,-aa[i].w);
        }
        for(i=1;i<tmp;i++)
            add(i,i+1,K,0);
        n=tmp+1;
        add(0,1,K,0),add(tmp,n,K,0);
        printf("%d\n",-EK());
    }
    return 0;
}


你可能感兴趣的:(POJ 3680 最小费用最大流)