【poj2152】Fire 树形DP

针对城市间的连接结构,本文提出了一种寻找消防站最优布局的算法。该算法通过动态规划的方式,考虑每个城市的成本和距离限制,计算出覆盖所有城市的最小总花费。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

Country Z has N cities, which are numbered from 1 to N. Cities are connected by highways, and there is exact one path between two different cities. Recently country Z often caught fire, so the government decided to build some firehouses in some cities. Build a firehouse in city K cost W(K). W for different cities may be different. If there is not firehouse in city K, the distance between it and the nearest city which has a firehouse, can’t be more than D(K). D for different cities also may be different. To save money, the government wants you to calculate the minimum cost to build firehouses.

Input

The first line of input contains a single integer T representing the number of test cases. The following T blocks each represents a test case.

The first line of each block contains an integer N (1 < N <= 1000). The second line contains N numbers separated by one or more blanks. The I-th number means W(I) (0 < W(I) <= 10000). The third line contains N numbers separated by one or more blanks. The I-th number means D(I) (0 <= D(I) <= 10000). The following N-1 lines each contains three integers u, v, L (1 <= u, v <= N,0 < L <= 1000), which means there is a highway between city u and v of length L.

Output

For each test case output the minimum cost on a single line.

Sample Input

5
5
1 1 1 1 1
1 1 1 1 1
1 2 1
2 3 1
3 4 1
4 5 1
5
1 1 1 1 1
2 1 1 1 2
1 2 1
2 3 1
3 4 1
4 5 1
5
1 1 3 1 1
2 1 1 1 2
1 2 1
2 3 1
3 4 1
4 5 1
4
2 1 1 1
3 4 3 2
1 2 3
1 3 3
1 4 2
4
4 1 1 1
3 4 3 2
1 2 3
1 3 3
1 4 2

Sample Output

2
1
2
2
3

Source

POJ Monthly,Lou Tiancheng


题意:给你一颗树,边上权值表示距离,一个点上可以建消防站,花费为 costi ,一个节点要么建消防站,要么周围 di 距离内必须有一个消防站,求覆盖全图的最小花费。

神一般的解法

ans[u] 为u的子树的答案, dp[u][v] 表示u节点被v保护的答案, dist[u][v] 表示u到v的距离。

因为 d[i] 不为负,所以可以看看v保护u时是否可以保护u的儿子,以此来更新。

具体来说就是若v可以保护u,则 dp[u][v]=cost[v]+min(dp[k][v]cost[v],ans[k]) ,其中k是u的儿子。具体来说就是先选上v,然后若v能保护k,则取min,减去cost[v]是因为在计算k的时候算了一遍,所以要剪掉防止重复。

若v不可以保护u,则dp[u][v]=INF。

算完之后, ans[u]=min{dp[u][i]}

要先递归,回溯的时候计算…

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;

const int INF = 1000000010;
const int SZ = 2010;

int cost[SZ],d[SZ],n;
int dist[SZ][SZ];

int head[SZ],nxt[SZ],tot = 1;

struct edge{
    int t,d;
}l[SZ];

void build(int f,int t,int d)
{
    l[++ tot].t = t;
    l[tot].d = d;
    nxt[tot] = head[f];
    head[f] = tot;
}

queue<int> q;

void getdist(int dist[],int s)
{
    dist[s] = 0;
    q.push(s);
    while(q.size())
    {
        int u = q.front(); q.pop();
        for(int i = head[u];i;i = nxt[i])
        {
            int v = l[i].t;
            if(!dist[v] && v != s)
                dist[v] = dist[u] + l[i].d,q.push(v);
        }
    }
}

int dp[SZ][SZ],ans[SZ];

void dfs(int u,int fa)
{
    for(int i = head[u];i;i = nxt[i])
    {
        int v = l[i].t;
        if(v == fa) continue;
        dfs(v,u);
    }   

    for(int v = 1;v <= n;v ++)
    {
        if(dist[u][v] <= d[u])
        {
            int tmp = 0;
            for(int i = head[u];i;i = nxt[i])
            {
                int k = l[i].t;
                if(k == fa) continue;
                tmp += min(dp[k][v] - cost[v],ans[k]);
            }
            dp[u][v] = cost[v] + tmp;
        }
        else
            dp[u][v] = INF;
    }
    for(int i = 1;i <= n;i ++)
        ans[u] = min(ans[u],dp[u][i]);
}

void init()
{
    tot = 1;
    memset(head,0,sizeof(head));
    memset(dp,0,sizeof(dp));
    memset(dist,0,sizeof(dist));
    memset(ans,63,sizeof(ans));
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T --)
    {
        init();
        scanf("%d",&n);
        for(int i = 1;i <= n;i ++)
            scanf("%d",&cost[i]);
        for(int i = 1;i <= n;i ++)
            scanf("%d",&d[i]);
        for(int i = 1;i <= n - 1;i ++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            build(a,b,c); build(b,a,c);
        }
        for(int i = 1;i <= n;i ++)
            getdist(dist[i],i);
        dfs(1,0);
        printf("%d\n",ans[1]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值