HDU 4009 Transfer water【最小树形图】

来源:互联网 发布:js比较数值大小 编辑:程序博客网 时间:2024/05/22 16:51

题目链接

题目意思

小A住在一个村庄,去年洪水淹没了这个村庄。因此他们决定将整个村庄搬到附近的山上。山上没有泉水,因此每一家必须选择打一个井或者从别的人家引水。如果一家决定去挖一口井,则他们打井的费用是他们房子的高度,X每米。如果决定去引水,如果引水的地方比当前地方高,则引水的钱是这两家的距离成上Y美元每米。如果引水的地方比当前低。则必须需要水泵(除了引水线后)还要花费Z美元买一个水泵。除此之外,村民的关系是需要考虑的。一些村民不允许一些村民从他们的房子引水。现在给出3个实数(a,b,c)。每个房子的坐标。计算整个村庄都通上水的最小花费。如果不能,则输出不能。

解题思路

这是一道最小树形图的题。因为每个点都可以选择打井或者是其他人家里引水,所以打井的位置是不能确定的。
这里就有一个巧妙的办法,我们建立一个人工节点0,将节点0到其他节点的权值赋成在节点打井的费用即可。要注意的是如果是从低处向高处引水,除了引水费用还要加上水泵的钱。
这样建立新图后就相当于求最小树形图。

代码部分

#include <iostream>#include <stdio.h>#include <string.h>#include <algorithm>#define INF 0x3f3f3f3fusing namespace std;int x,y,n,z;struct node{    int x;    int y;    int z;} p[1002];struct Edge{    int u;    int v;    long long int w;} edge[1002*1002];long long int dis(node a,node b){    long long int temp=abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z);    return temp;}///closest[i]表示距离i最近的点,vis标记数组,in[i]存放节点的入边最小权值,id[i]存放节点的编号int closest[1005],vis[1005],in[1005],id[1005];int minroot;long long int min_cost(int root,int nodenum,int edgenum){    long long int ans=0;    int i;    while(true)    {        for(i=0; i<nodenum; i++)            in[i]=INF;        for(i=0; i<edgenum; i++)        {            int u=edge[i].u;            int v=edge[i].v;            ///u到v的权值如果比当前v的入边最小权还小,就更新最小权值,更新到v的最近的点为u            if(u!=v&&edge[i].w<in[v])            {                in[v]=edge[i].w;                closest[v]=u;                //if(u==root)///某个点的前驱节点是虚拟节点                //  minroot=i;            }        }        for(i=0; i<nodenum; i++)        {            if(i==root)                continue;            if(in[i]==INF)                return -1;        }        int cnt=0;        memset(vis,-1,sizeof(vis));        memset(id,-1,sizeof(id));        in[root]=0;        for(i=0; i<nodenum; i++)        {            ans+=in[i];            int t=i;            ///从t开始一直往前找,如果有环则会回到自身            while(vis[t]!=i&&id[t]==-1&&t!=root)            {                vis[t]=i;                t=closest[t];            }            if(t!=root&&id[t]==-1)            {                for(int u=closest[t]; u!=t; u=closest[u])                {                    id[u]=cnt;                }                id[t]=cnt++;            }        }        if(cnt==0)            break;        for(i=0; i<nodenum; i++)///为不在环中的节点编号        {            if(id[i]==-1)                id[i]=cnt++;        }        for(i=0; i<edgenum; i++)        {            int s=edge[i].u;            int t=edge[i].v;            edge[i].u=id[s];            edge[i].v=id[t];            if(s!=t)                edge[i].w=edge[i].w-in[t];        }        nodenum=cnt;        root=id[root];    }    return ans;}int main(){    int t,no;    while(~scanf("%d%d%d%d",&n,&x,&y,&z))    {        if(n==0&&x==0&&y==0&&z==0)            break;        for(int i=1; i<=n; i++)            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);        int k=0;        for(int i=1; i<=n; i++)        {            edge[k].u=0;            edge[k].v=i;            edge[k].w=x*p[i].z;            k++;        }        for(int i=1; i<=n; i++)        {            scanf("%d",&t);            for(int j=1; j<=t; j++)            {                scanf("%d",&no);                edge[k].u=i;                edge[k].v=no;                edge[k].w=dis(p[i],p[no])*y;                if(p[i].z<p[no].z)                {                    edge[k].w+=z;                }                k++;            }        }        long long int ans=min_cost(0,n+1,k);        if(ans<INF)            printf("%lld\n",ans);        else            printf("poor XiaoA\n");    }    return 0;}
原创粉丝点击