poj 2152解题报告

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
题目大意:
给你一张城市图(他是一棵树) 城市之间由高速公路连接,每条高速公路长度可能不一样。对于城市i,在这个城市建造一个防火站的花费为w[i], 然后必须要在d[i]范围内找到一个站(包括他自己)j,至少存在一个这样的j站,在j站修建一个防火站,那么i 就可以依赖于j。
我们的目的就是求出这样的一张网络,每个城市都有依赖的防火站,且花费要求最小。

分析:
一开始,拿到这道题一头雾水。只能想到np的算法。后来看了下陈启峰的一张一弛解题之道,才大概会这种类型的题目。
看一下数据范围1000,显然是n^2的算法,因此,我们先用spfa预处理出任意两点之间的距离dis[u][v], 然后再用树的点分治思想,在求解一颗树的时候,我们只关注这个树的根节点和他的儿子节点,不关注这颗树的父亲,这样,就不用考虑后效性问题了。
然后,我们可以尝试定义状态dp[i],但是这样定义之后,显然是没办法转移的,因为并不知道i 依赖哪个站。常用的办法就是再加一维,及dp[i][j]。
由上,状态就可以为dp[i][j]表示以i 为根的子树,第i 个城市一定依赖于j 且i 子树中的其他节点一定都有所依赖的节点的最小花费。这句话 两个一定就是紧,缩小整个解空间的范围,但是我们得先证明这样的状态一定是正确的。(具体见论文)
那么问题来了,转移是怎样的呢?
观察到,每次计算以i 为根的子树的时候,都要枚举1 - n节点。 如果我们暴力的搞,每次i 节点要枚举n,他的儿子有很多种决策,其中一种是以i 树内的节点作为依赖站, 其他种是以i 树以外的节点作为依赖站,这还是np复杂度的。想到这我发现问题又回到了原来的地方。Orz
让我们仔细分析一下,每次对根节点 i 枚举依赖站v的时候,那么i 站一定修了防火站,他的资金已经花费了,如果i 的儿子节点能够以v作为依赖站那么就没有比以v作为依赖站更优的情况如果i 的儿子节点不能以v作为依赖站,那么就可以假定i 的儿子一定是以 以他为根的子树中的节点作为依赖站的,因此,设计一个辅助dp数组best[i]表示 以i 为根的树,已经满足题目要求的最小花费。
这样设定,限制了一些条件,就又缩小了解空间的范围,使得我们能够在n^2的速度内求解。
最后转移自然也出来了。
dp[u][j] = w[j] + sigma( min(dp[v][j] - w[j], best[v]);
best[u] = min(best[i], dp[u][j]);

//
//  Created by Running Photon on 2015-09-07
//  Copyright (c) 2015 Running Photon. All rights reserved.
//
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <sstream>
#include <set>
#include <vector>
#include <stack>
#define ALL(x) x.begin(), x.end()
#define INS(x) inserter(x, x,begin())
#define ll long long
#define CLR(x) memset(x, 0, sizeof x)
#define MAXN 9999
#define MAXSIZE 10
#define DLEN 4
using namespace std;
const int inf = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 1e4 + 10;
const int maxv = 1e3 + 10;
const double eps = 1e-9;


inline int read() {
    char c = getchar();
    int f = 1;
    while(!isdigit(c)) {
    if(c == '-') f = -1;
    c = getchar();
    }
    int x = 0;
    while(isdigit(c)) {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x * f;
}
int w[maxv], d[maxv];
int head[maxv], nxt[maxn], pnt[maxn], cost[maxn], cnt;
int dp[maxv][maxv], dis[maxv][maxv], best[maxv];
int n;
void add_edge(int u, int v, int val) {
    cost[cnt] = val;
    pnt[cnt] = v;
    nxt[cnt] = head[u];
    head[u] = cnt++;
}
int vis[maxv];
void spfa(int root) {
    for(int i = 1; i <= n; i++) {
        dis[root][i] = inf;
    }
    dis[root][root] = 0;
    queue <int> que;
    que.push(root);
    vis[root] = 1;
    while(que.size()) {
        int u = que.front();
        que.pop();
        vis[u] = 0;
        for(int i = head[u]; ~i; i = nxt[i]) {
            int v = pnt[i];
            if(dis[root][v] > dis[root][u] + cost[i]) {
                dis[root][v] = dis[root][u] + cost[i];
                if(!vis[v]) {
                    vis[v] = 1;
                    que.push(v);
                }
            }
        }
    }
}
void dfs(int u, int fa) {
    for(int i = head[u]; ~i; i = nxt[i]) {
        int v = pnt[i];
        if(v == fa) continue;
        dfs(v, u);
    }
    for(int i = 1; i <= n; i++) {
        if(dis[u][i] <= d[u]) {
            dp[u][i] = w[i];
            for(int j = head[u]; ~j; j = nxt[j]) {
                int v = pnt[j];
                if(v == fa) continue;
                dp[u][i] += min(dp[v][i] - w[i], best[v]);
            }
            best[u] = min(best[u], dp[u][i]);
        }
    }
}
int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    int cas = read();
    while(cas--) {
        CLR(vis); memset(best, 0x3f, sizeof best);
        cnt = 0; memset(head, -1, sizeof head);
        memset(dp, 0x3f, sizeof dp);
        n = read();
        for(int i = 1; i <= n; i++)
            w[i] = read();
        for(int i = 1; i <= n; i++)
            d[i] = read();
        for(int i = 1; i < n; i++) {
            int u = read(), v = read(), val = read();
            add_edge(u, v, val);
            add_edge(v, u, val);
        }
        for(int i = 1; i <= n; i++)
            spfa(i);
        CLR(vis);
        dfs(1, 1);
        printf("%d\n", best[1]);
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值