您的位置:首页 > 理论基础 > 计算机网络

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

2013-03-04 22:13 357 查看
题目链接: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;
                else
					flow[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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: