最小树形图(hdu4009)

时间:2022-10-13 16:01:48

Transfer water

Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others)
Total Submission(s): 3821    Accepted Submission(s): 1371
Problem Description
XiaoA lives in a village. Last year flood rained the village. So they decide to move the whole village to the mountain nearby this year. There is no spring in the mountain, so each household could only dig a well or build a water line from other household. If the household decide to dig a well, the money for the well is the height of their house multiplies X dollar per meter. If the household decide to build a water line from other household, and if the height of which supply water is not lower than the one which get water, the money of one water line is the Manhattan distance of the two households multiplies Y dollar per meter. Or if the height of which supply water is lower than the one which get water, a water pump is needed except the water line. Z dollar should be paid for one water pump. In addition,therelation of the households must be considered. Some households may do not allow some other households build a water line from there house. Now given the 3‐dimensional position (a, b, c) of every household the c of which means height, can you calculate the minimal money the whole village need so that every household has water, or tell the leader if it can’t be done.

Input
Multiple cases.
First line of each case contains 4 integers n (1<=n<=1000), the number of the households, X (1<=X<=1000), Y (1<=Y<=1000), Z (1<=Z<=1000).
Each of the next n lines contains 3 integers a, b, c means the position of the i‐th households, none of them will exceeded 1000.
Then next n lines describe the relation between the households. The n+i+1‐th line describes the relation of the i‐th household. The line will begin with an integer k, and the next k integers are the household numbers that can build a water line from the i‐th household.
If n=X=Y=Z=0, the input ends, and no output for that.

Output
One integer in one line for each case, the minimal money the whole village need so that every household has water. If the plan does not exist, print “poor XiaoA” in one line.

Sample Input
  
  
  
2 10 20 30 1 3 2 2 4 1 1 2 2 1 2 0 0 0 0
 

Sample Output
  
  
  
30
Hint
In 3‐dimensional space Manhattan distance of point A (x1, y1, z1) and B(x2, y2, z2) is |x2‐x1|+|y2‐y1|+|z2‐z1|.
题意:在山上有n户人家,给出他们的坐标(x,y,z)z是海拔;每户人家的水来源有地下水,或从其他人家引进来,如果打地下水每米X元,深度是海拔,如果从他家引水,有两种情况,一是供水人家海拔较高,费用是每米Y元,距离是曼哈顿距离,二是海拔较低,需要水泵,一个水泵需要额外花费Z元,问要是每家人都有水,至少花费是多少?
分析:水的最终来源肯定是地下水,所以至少有一家人是从地下获得的水,所以n户人家编号1到n,地下水编号n+1,地下水到每户建边,花费是L*Z,然后按照题目中的关系把相应的人家建边,费用为(Z)+L*Y;地下水就是根节点,运行一下最小树形图即可:
程序:
#include"string.h"
#include"stdio.h"
#include"math.h"
#include"queue"
#define eps 1e-10
#define M 1009
#define inf 100000000
using namespace std;
struct node
{
    int x,y,z;
}p[M];
struct edge
{
    int u,v;
    int w;
}edge[M*M];
int pre[M],id[M],use[M],in[M];
int Fabs(int x)
{
    return x>0?x:-x;
}
int mini_tree(int root,int n,int m)//分别是树根,节点数,边数,序号从1开始
{
    int ans=0;
    int i,u;
    while(1)
    {
        for(i=1;i<=n;i++)
            in[i]=inf;
        for(i=1;i<=m;i++)
        {
            int u=edge[i].u;
            int v=edge[i].v;
            if(edge[i].w<in[v]&&u!=v)
            {
                in[v]=edge[i].w;
                pre[v]=u;
            }
        }//找最小的入边
        for(i=1;i<=n;i++)
        {
            if(i==root)continue;
            ans+=in[i];//把边权加起来
            if(in[i]==inf)//如果存在没有入弧的点则不存在最小树形图
                return -1;
        }
        memset(id,-1,sizeof(id));
        memset(use,-1,sizeof(use));
        int cnt=0;
        for(i=1;i<=n;i++)//枚举每个点,搜索找环
        {
            int v=i;
            while(v!=root&&use[v]!=i&&id[v]==-1)
            {
                use[v]=i;
                v=pre[v];
            }
            if(v!=root&&id[v]==-1)//当找到环的时候缩点编号
            {
                ++cnt;
                id[v]=cnt;
                for(u=pre[v];u!=v;u=pre[u])
                    id[u]=cnt;
            }
        }
        if(cnt==0)//如果没有环结束程序
            break;
        for(i=1;i<=n;i++)//把余下的不在环里的点编号
            if(id[i]==-1)
                id[i]=++cnt;
        for(i=1;i<=m;i++)//建立新的图
        {
            int u=edge[i].u;
            int v=edge[i].v;
            edge[i].u=id[u];
            edge[i].v=id[v];
            if(edge[i].u!=edge[i].v)
                edge[i].w-=in[v];
        }
        n=cnt;//更新节点数和根节点的编号
        root=id[root];
    }
    return ans;
}
int main()
{
    int n,X,Y,Z,i,j;
    while(scanf("%d%d%d%d",&n,&X,&Y,&Z),n||X||Y||Z)
    {
        for(i=1;i<=n;i++)
            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
        int m=0;
        for(i=1;i<=n;i++)
        {
            int k;
            scanf("%d",&k);
            while(k--)
            {
                scanf("%d",&j);
                if(i==j)continue;
                m++;
                edge[m].u=i;
                edge[m].v=j;
                if(p[j].z>p[i].z)
                    edge[m].w=Z+Y*(Fabs(p[i].x-p[j].x)+Fabs(p[i].y-p[j].y)+Fabs(p[i].z-p[j].z));
                else
                    edge[m].w=Y*(Fabs(p[i].x-p[j].x)+Fabs(p[i].y-p[j].y)+Fabs(p[i].z-p[j].z));
            }
        }
        for(i=1;i<=n;i++)
        {
            m++;
            edge[m].u=n+1;
            edge[m].v=i;
            edge[m].w=p[i].z*X;
        }
        int ans=mini_tree(n+1,n+1,m);
        printf("%d\n",ans);
    }
    return 0;
}