HDU 4009 Transfer water(不定根最小树形图)

题目大意:一个村庄被洪水摧毁了,整个村庄都要转移。但是山上没有泉水,每户家庭只能在自家挖一个水井或是修一个水渠从别的家庭引水。如果要修井,则修井费用和房子所在海拔高度有关,每米X元。如果从别人的家里引水,如果从高于自己家高度的人家里引水,费用为每米Y元。如果从低于自己家高度的人家里引水,每条要多花费Z元。现在给你这个村庄N个家庭房屋的坐标(a,b,c)和三种花费X,Y,Z。接着给你各家之间能单向修建引水沟渠的限制。问:能使全村庄的人喝上水的总修建费用最低为多少。若不能,则输出"poor XiaoA"

因为每家都可以打井,所以poor XiaoA这种输出是没有的...然后因为每家都可以打井,不能自己链接到自己吧..设置一个虚根,把每家打井的话费当做边权建立边,求得最小树形图即可,和上一道题类似。

因为我的模板是点和边都是从1开始...然后当时边数从1每次m++...最后的时候传递给函数的参数是m的话...就会多读取一条边...随机变量然后各种出错...修改了各种姿势才知道这个...

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<iostream>
using namespace std;
#define inf 0x7fffffff
struct point
{
    int x,y,z;
}p[1020];
struct node
{
    int u,v,w;
} edge[1000200];
int pre[1020],in[1020],id[1020],use[1020];
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 n,x,y,z;
int getdis(point a,point b)
{
    int dis=abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z);
    dis=dis*y;
    if(a.z>=b.z)
        return dis;
    return dis+z;
}
int main()
{
    while(scanf("%d %d %d %d",&n,&x,&y,&z),n||x||y||z)
    {
        for(int i=1;i<=n;i++)
            scanf("%d %d %d",&p[i].x,&p[i].y,&p[i].z);
        int m=1,k,j;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&j);
            while(j--)
            {
                scanf("%d",&k);
                edge[m].u=i;
                edge[m].v=k;
                edge[m].w=getdis(p[i],p[k]);
                m++;
            }
        }
        for(int i=1;i<=n;i++)
        {
            edge[m].u=n+1;
            edge[m].v=i;
            edge[m].w=p[i].z*x;
            m++;
        }
        int ans=mini_tree(n+1,n+1,m-1);
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值