Hdu Base Station (网络流最大权闭合图)

来源:互联网 发布:国考行测技巧知乎 编辑:程序博客网 时间:2024/05/14 17:39

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3879

题意:要在 n 个城市修建电台,已知修建每个电台的费用。然后有 m 行,每行三个数 a,b,c,表示如果城市 a 和城市 b 都修建了电台,将带来 c 的收益。求选择一些城市修建电台使得总收益最大。 (0<n<=5000,0<m<=50000).

思路:典型的最大权闭合图。原理参见:http://www.byvoid.com/blog/noi-2006-profit/

#include <cstdio>#include <cstring>#include <iostream>using namespace std;const int INF = 0x6fffffff ;   //权值上限const int MAXPT = 57000 ;           //顶点数上限const int MAXEG = 1000000 ; //边数上限const int MAXQUE = MAXPT*2 ;      // 队列长度/*    s = 1 ; // 源点    t = n ; // 汇点*/class MNF_SAP{private:    //int m,n;    int s,t;    int dis[MAXPT];   //距离标号    int pre[MAXPT];   //前置顶点    int flow[MAXPT];  //到当前点为止,所有弧的最小值    int curedge[MAXPT];   //当前弧cur    int cnt[MAXPT];   //k标号出现次数    int queue[MAXQUE],front,rear;    bool vis[MAXPT];    void BFS ()    {        int i,u;        memset(vis,false,sizeof(vis));        front=rear=0;        dis[t]=0;        vis[t]=true;        queue[++rear]=t;        while (front!=rear)        {            u=queue[(++front)%MAXQUE];            for (i=head[u];i!=0;i=edges[i].next)                if (vis[edges[i].v]==false && !edges[i].cap)                {                    dis[edges[i].v]=dis[u]+1;                    vis[edges[i].v]=true;                    queue[(++rear)%MAXQUE]=edges[i].v;                }        }for (i=1;i<=n;i++)cnt[dis[i]]++;    }public:    struct Node    {        int v,cap,next;Node(){}Node (int _v,int _cap,int _next){v=_v;cap=_cap;next=_next;}    }edges[MAXEG];    int n;      //总节点数int e;    int head[MAXPT];//    MNF_SAP(){}//    ~MNF_SAP(){}void init (int temp){n=temp;e=2;memset (head,0,sizeof(head));}void Add (int u,int v,int cap)   //始,终,量{edges[e]=Node(v,cap,head[u]);head[u]=e++;edges[e]=Node(u,0,head[v]);head[v]=e++;}    int SAP ()    {        int u,v,i,maxflow=0;   //总最大流        s=1;        t=n;        u=s;        flow[s]=INF;        for (i=1;i<=n;i++)curedge[i]=head[i];     //当前弧初始化        BFS ();        cnt[0]=n;        while (dis[s]<n)        {            for (i=curedge[u];i!=0;i=edges[i].next)        //找允许弧                if (edges[i].cap && dis[edges[i].v]+1==dis[u])break;            if (i!=0)      //存在允许弧            {                curedge[u]=i;         //设置当前弧                v=edges[i].v;                if (edges[i].cap<flow[u])flow[v]=edges[i].cap;                elseflow[v]=flow[u];  //标记当前顶点为止经过的最小弧                u=v;                pre[v]=i;  //前置顶点边号                if (u==t)                {                    do                    {                        edges[pre[u]].cap-=flow[t];          //正向弧减a[t]                        edges[pre[u]^1].cap+=flow[t];        //通过异或操作找反向弧                        u=edges[pre[u]^1].v;                    }                    while (u!=s);                    maxflow+=flow[t];                    //memset(flow,0,sizeof(flow));                    flow[s]=INF;                }            }            else   //不存在允许弧            {                if (--cnt[dis[u]]==0)break;     //间隙优化                dis[u]=n;                curedge[u]=head[u];                for (i=head[u];i!=0;i=edges[i].next)                    if (edges[i].cap && dis[edges[i].v]+1<dis[u])                        dis[u]=dis[edges[i].v]+1;       //修改距离标号为 最小的非允许弧加1                cnt[dis[u]]++;                if (u!=s)u=edges[pre[u]^1].v;            }        }        return maxflow;    }};MNF_SAP ob;int main (){    int n,m,i;    int a,b,c;    while (~scanf("%d%d",&n,&m))    {int s=1;int t=n+m+2;int sum=0;ob.init(t);for (i=1;i<=n;i++){scanf("%d",&a);ob.Add(s+m+i,t,a);}        for (i=1;i<=m;i++)        {            scanf("%d%d%d",&a,&b,&c);ob.Add(s,s+i,c);ob.Add(s+i,s+m+a,INF);ob.Add(s+i,s+m+b,INF);sum+=c;        }printf("%d\n",sum-ob.SAP());    }    return 0;}