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

题目链接:
HDU 4009 Transfer water
题意:
需要给n户人家通水。给出每户人家的三维坐标,每户人家自建一口井的花费是X,对于每户人家给出k[i]条边,to[j],表示可以从第u=i户人家铺设一条水管道到第v=to[j]户人家,铺设的费用是两户人家三维坐标之差的绝对值之和*Y,如果u的高度小于v的高度还需要额外加上Z花费。问将n户人家都通上水的最小花费是多少?如果不能使每户人家都通上水就输出-1.
分析:
实际上不会输出-1的,因为每户人家都可以自建一口井。。。
每户人家之间的建边就按照题目叙述的那样建立边权就好了,主要是每户人家可以自建一口井花费X如何用边和边权表示?
可以新设一个根节点为0,从0到每户人家建立一条权值为X的边表示自建水井,这样子跑一下最小树形图就好了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
const int MAX_N = 2010;
const int MAX_M = 1001000;
const int INF = 0x7fffffff;

int n, X, Y, Z, K, NV, NE;
int vis[MAX_N], pre[MAX_N], ID[MAX_N];
ll In[MAX_N];

struct Edge{
    int u, v;
    ll w;

    Edge() {}
    Edge(int _u, int _v, ll _w) : u(_u), v(_v), w(_w) { }
}edge[MAX_M];

struct Point{
    int x, y, z;

    Point() {}
    Point(int _x, int _y, int _z) :x(_x), y(_y), z(_z) {}

    int dis(const Point& rhs) const {
        return abs(x - rhs.x) + abs(y - rhs.y) + abs(z - rhs.z);
    }
}point[MAX_N];

ll ZLEdmonds(int root, int NV, int NE)
{
    ll res = 0, w;
    int u, v;
    while(1){
        for(int i = 0; i < NV; i++){
            In[i] = INF;
        }
        for(int i = 0; i < NE; i++){
            u = edge[i].u, v = edge[i].v, w = edge[i].w;
            if(u != v && w < In[v]){
                In[v] = w;
                pre[v] = u;
            }
        }
        for(int i = 0; i < NV; i++){
            if(i != root && In[i] == INF) return -1;
        }
        int cnt = 0;
        memset(vis, -1, sizeof(vis));
        memset(ID, -1, sizeof(ID));
        In[root] = 0;
        for(int i = 0; i < NV; i++){
            res += In[i];
            v = i;
            while(v != root && ID[v] == -1 && vis[v] != i){
                vis[v] = i;
                v = pre[v];
            }
            if(v != root && ID[v] == -1){
                for(u = pre[v]; u != v; u = pre[u]){
                    ID[u] = cnt;
                }
                ID[v] = cnt ++;
            }
        }
        if(cnt == 0) break;
        for(int i = 0; i < NV; i++){
            if(ID[i] == -1) ID[i] = cnt++;
        }
        for(int i = 0; i < NE; i++){
            v = edge[i].v, u = edge[i].u;
            edge[i].u = ID[u], edge[i].v = ID[v];
            if(edge[i].u != edge[i].v){
                edge[i].w -= In[v];
            }
        }
        NV = cnt;
        root = ID[root];
    }
    return res;
}

int main()
{
    IOS;
    while(~scanf("%d%d%d%d", &n, &X, &Y,&Z) && (n || X || Y || Z)){
        NE = 0;
        for(int i = 1; i <= n; i++){
            scanf("%d%d%d", &point[i].x, &point[i].y, &point[i].z);
        }
        for(int i = 1; i <= n; i++){
            scanf("%d", &K);
            int u = i;
            for(int j = 0; j < K; j++){
                int v;
                scanf("%d", &v);
                if(u == v) continue;
                ll w = point[u].dis(point[v]) * Y;
                if(point[u].z < point[v].z) w += Z;
                edge[NE++] = Edge(u, v, w);
            }
        }
        int root = 0;
        for(int i = 1; i <= n; i++){
            edge[NE++] = Edge(root, i, point[i].z * X);
        }
        NV = n + 1;
        ll ans = ZLEdmonds(root, NV, NE);
        if(ans == -1) printf("poor XiaoA\n");
        else printf("%lld\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值