avatar
fireworks99
keep hungry keep foolish

HDU 4009 Transfer water(Minimun tree-shape graph)

Description

N个房子搬到山上,要解决供水问题有两种方案:

①自己打井,耗钱 (X * 房子高度)

②从别家引水,耗钱 (Y * 两房子的曼哈顿距离),如果源较低,再加一个水泵钱Z

求解决所有房子供水问题的最小花费

Analyze

不难发现是一个不定根的题目 -> 增加一个伪根。

但是在建图时,“饮水”的边正常建立,“打井”如何处理?看似“自给自足”让人想到自环,可两者没什么关系,添自环不解决问题。

而且对于独立点,即使不能生成最小树形图,也可以”打井”完成任务,这种情况如何求最小花费?

Solution

将伪根连向各点的边的权值设为该点打井花费!

实现了“打井”与“引水”一视同仁,可计算最小花费。

什么完美的建模思想!

这跟之前做过的一道最短路相似,那题是“昂贵的聘礼”,给定了终点,也是不定根(起点任选),求最短路。但是不仅边有权值,每个点也有权值,意味着从每个点出发的花费不同,这时建立超级源点连向各点的边的权值就应该设为各点权值!

这是一类“不定根 && 点有权值”的图论题目

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1005;
const int INF = 0x3f3f3f3f;

int n, m, X, Y, Z, id[N], pre[N], vis[N], in[N], x[N], y[N], z[N];

struct edge
{
    int u, v, w;
}e[N * N];///Pay attention to the number of edges!

int getDis(int a, int b)
{
    return Y * (abs(x[a] - x[b]) + abs(y[a] - y[b]) + abs(z[a] - z[b])) + (z[b] > z[a] ? Z :0);
}

int zhuliu(int root)
{
    int u, v, res = 0;
    while(true)
    {
        ///1.Set of in
        for(int i = 1; i <= n; ++i)
            in[i] = -1;
        for(int i = 1; i <= m; ++i)
            if(e[i].u != e[i].v && (e[i].w < in[ e[i].v ] || in[ e[i].v ] < 0))
                in[ e[i].v ] = e[i].w, pre[ e[i].v ] = e[i].u;
        for(int i = 1; i <= n; ++i)
            if(i != root && in[i] < 0)
                return -1;

        ///2.Directed circle
        int cnt = 1;
        memset(id, -1, sizeof(id));
        memset(vis,-1, sizeof(vis));
        in[root] = 0;
        for(int i = 1; i <= n; ++i)
        {
            res += in[i];
            v = i;
            while(id[v] == -1 && v != root && vis[v] != i)
                vis[v] = i, v = pre[v];
            if(id[v] == -1 && v != root)///   vis[v] == i
            {
                for(u = pre[v]; u != v; u = pre[u])
                    id[u] = cnt;
                id[v] = cnt++;
            }
        }

        ///3.No circle -> Over
        if(cnt == 1)
            break;

        ///4.Process other nodes ,and then "shrink".
        for(int i = 1; i <= n; ++i)
            if(id[i] == -1)
                id[i] = cnt++;
        for(int i = 1; i <= m; ++i)
        {
            v = e[i].v;
            e[i].u = id[ e[i].u ];
            e[i].v = id[ e[i].v ];
            if(e[i].u != e[i].v)
                e[i].w -= in[v];///This v is the original v!
        }
        n = cnt - 1;
        root = id[root];
    }
    return res;
}

int main()
{
    while(~scanf("%d %d %d %d", &n, &X, &Y, &Z) && !(n + X + Y + Z == 0))
    {
        for(int i = 1; i <= n; ++i)
        {
            scanf("%d %d %d", &x[i], &y[i], &z[i]);
            e[i].u = n + 1, e[i].v = i, e[i].w = z[i] * X;
        }

        m = n;
        int tem, num;
        for(int i = 1; i <= n; ++i)
        {
            scanf("%d", &num);
            while(num--)
            {
                scanf("%d", &tem);
                e[++m].u = i, e[m].v = tem, e[m].w = getDis(i, tem);
            }
        }

        int ans = zhuliu(++n);
        printf("%d\n", ans);
    }
    return 0;
}
Site by Baole Zhao | Powered by Hexo | theme PreciousJoy