【POJ 2152】Fire【树形DP】

题意:给定n个节点组成的树,树有边权.现在要在一些点上建立消防站,每个点建站都有个cos[i],如果不在当前的点上建站,也要依赖其他的消防站,并且距离不超过lim[i]。求符合上述条件的最小费用建站方案。n <= 1000.

思路:树形DP,dp[i][j]表示以i为根的子树满足条件,并且i依赖于j节点的消防站所需的最小代价。base[i]为以i为根的子树满足条件所需要的最小代价。转移方程为:dp[i][j] = cos[j]+∑min(dp[k][j]-cos[j], base[k]), base[i] = min(dp[i][j])。因为i依赖于j,所以子树中如果有依赖于j的状态则需要减去一次cos[j],否则会多算代价。

复杂度为O(n^2)。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define N 1003

struct E{
    int v, d, ne;
    E(){}
    E(int _v, int _d, int _ne):v(_v), d(_d), ne(_ne){}
}e[N*N*2];

int n, size, head[N], lim[N], cos[N], dis[N][N];
int dp[N][N], base[N];

void init() {
    size = 0;
    memset(head, -1, sizeof(head));
    memset(dp, 0x3f, sizeof(dp));
    memset(base, 0x3f, sizeof(base));
}

void add(int u, int v, int d) {
    e[size] = E(v, d, head[u]);
    head[u] = size++;
}

void cal(int u, int fa, int top, int d) {
    dis[top][u] = d;
    int i, v;
    for (i = head[u];~i;i = e[i].ne) {
        v = e[i].v;
        if (v == fa) continue;
        cal(v, u, top, d+e[i].d);
    }
}

void dfs(int u, int f) {
    int i, v, j;
    vector<int>V;
    for (i = head[u];~i;i = e[i].ne) {
        v = e[i].v;
        if (v == f) continue;
        dfs(v, u);
        V.push_back(v);
    }
    for (i = 1;i <= n;i++) {
        if (dis[u][i] > lim[u]) continue;
        dp[u][i] = cos[i];
        for (j = 0;j < V.size();j++) {
            dp[u][i] += min(dp[V[j]][i]-cos[i], base[V[j]]);
        }
        base[u] = min(base[u], dp[u][i]);
    }
}

int main() {
    int T, i, j, u, v, d;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        init();
        for (i = 1;i <= n;i++) {
            scanf("%d", &cos[i]);
        }
        for (i = 1;i <= n;i++) {
            scanf("%d", &lim[i]);
        }
        for (i = 1;i < n;i++) {
            scanf("%d%d%d", &u, &v, &d);
            add(u, v, d), add(v, u, d);
        }
        for (i = 1;i <= n;i++) {
            cal(i, -1, i, 0);
        }
        dfs(1, -1);
        printf("%d\n", base[1]);
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值