hdu 4009 Transfer water 最小树形图

来源:互联网 发布:股票涨停公式源码 编辑:程序博客网 时间:2024/06/05 18:48
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4009
题目大意:

在山上有N户人家,每家的坐标为(xi, yi, zi)。每户人家要吃水,要么自己打井,花费为A zi,要么从别人的家引水渠代价为B两家的曼哈顿距离,如果这家的海拔比供水的低,还要另外再买一个价值为C的水泵。问每家都有水吃的最低花费是多少。

每家都有水的目标一定能达成,大不了每家都打口井…

把所有能连水渠的两家连起来,就得到了一个有向图,答案一定一个森林。建立一个虚节点,向每个点连一条有向边,权值为在第i家挖井的代价,这样就把答案从森林变成了一棵树,求全图的一个最小树形图就是最低的代价。


学习了一下朱刘算法,算法思路还比较简单,实现起来很巧妙。算是一个模板吧。

#include<bits/stdc++.h>#define N 1100#define INF 0x3f3f3f3f#define M 1100000using namespace std;int pre[N],id[N],visit[N],in[N];struct node{    int u,v,cost;}edge[M];int zhuliu(int root,int n,int m){    int res=0,u,v;    while(1)    {        for(int i=0;i<n;i++)    in[i]=INF;        for(int i=0;i<m;i++)            if(edge[i].u!=edge[i].v&&edge[i].cost<in[edge[i].v])            {                pre[edge[i].v]=edge[i].u;                in[edge[i].v]=edge[i].cost;            }        for(int i=0;i<n;i++)            if(i!=root&&in[i]==INF)                return -1;        int tn=0;        memset(id,-1,sizeof(id));        memset(visit,-1,sizeof(visit));        in[root]=0;        for(int i=0;i<n;i++)        {            res+=in[i];            v=i;            while(visit[v]!=i&&id[v]==-1&&v!=root)            {                visit[v]=i;                v=pre[v];            }            if(v!=root&&id[v]==-1)            {                for(int u=pre[v];u!=v;u=pre[u])                    id[u]=tn;                id[v]=tn++;            }        }        if(tn==0)   break;        for(int i=0;i<n;i++)            if(id[i]==-1)                id[i]=tn++;        for(int i=0;i<m;)        {            v=edge[i].v;            edge[i].u=id[edge[i].u];            edge[i].v=id[edge[i].v];            if(edge[i].u!=edge[i].v)                edge[i++].cost-=in[v];            else                swap(edge[i],edge[--m]);        }        n=tn;        root=id[root];    }    return res;}int n,x[N],y[N],z[N],a,b,c,cnt;void addedge(int u,int v,int cost){    edge[cnt].u=u;    edge[cnt].v=v;    edge[cnt++].cost=cost;}int main(){    while(~scanf("%d%d%d%d",&n,&a,&b,&c)&&n)    {        cnt=0;        for(int i=1;i<=n;i++)        {            scanf("%d%d%d",&x[i],&y[i],&z[i]);            addedge(0,i,a*z[i]);        }        for(int i=1;i<=n;i++)        {            int t;            scanf("%d",&t);            for(int j=0;j<t;j++)            {                int u;                scanf("%d",&u);                if(u==i) continue ;                int cost=(abs(x[i]-x[u])+abs(y[i]-y[u])+abs(z[i]-z[u]))*b;                if(z[i]<z[u])   addedge(i,u,cost+c);                else addedge(i,u,cost);            }        }        cout<<zhuliu(0,n+1,cnt)<<endl;    }}



0 0