Fire (poj 2152 树形dp)

Fire (poj 2152 树形dp)

给定一棵n个结点的树(1<n<=1000)。现在要选择某些点,使得整棵树都被覆盖到。当选择第i个点的时候,可以覆盖和它距离在d[i]之内的结点,同时花费为v[i]。问最小花费。

以前做过一道类似的题(水库),这道题也差不多。首先来考虑,用\(best[i]\)表示以i为根的子树的最小花费。这样做有什么问题呢?它无法很好的处理消防站重复建的问题。

所以换一种做法。\(best[i]\)依然表示原来的含义,新建一个数组\(f[i][j]\),表示当i这个结点,依赖j的消防站时的最小花费。转移方程就是:\(f[i][j]=v[i]+\sum min(f[son_k][j]-v[j], best[son_k])\)。注意当\(dis(i, j)>d[i]\)时,\(f[i][j]=\infty\)。它的思想就是如果i依赖j,就直接让子树中依赖j的点都减去依赖,从而消除影响。

(傻逼了,用rmq求树上两点距离)

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

const int maxn=1005, maxm=1005, logn=12, INF=1e9;

struct Graph{
    struct Edge{
        int to, next, v; Graph *bel;
        inline int operator*(){ return to; }
        Edge& operator ++(){
            return *this=bel->edge[next]; }
    }edge[maxm*2];
    void reset(){
        memset(fir, 0, sizeof(fir)); cntedge=0; }
    void addedge(int x, int y, int v){
        Edge& e=edge[++cntedge];
        e.to=y; e.next=fir[x]; e.v=v;
        e.bel=this; fir[x]=cntedge; }
    Edge& getlink(int x){ return edge[fir[x]]; }
    int cntedge, fir[maxn];
}g;

//初始化及树形dp
int T, n, mi[logn], w[maxn], d[maxn];
int best[maxn], dp[maxn][maxn];
//求两点距离(rmq lca)
int id[maxn*2], fir[maxn], dep[maxn], time;
int st[maxn*2][logn];

void predfs(int now, int par){
    Graph::Edge e=g.getlink(now);
    id[++time]=now; fir[now]=time;
    for (; *e; ++e){
        if (*e==par) continue;
        dep[*e]=dep[now]+e.v; predfs(*e, now);
        id[++time]=now;
    }
}

int mindep(int x, int y){
    return (dep[x]<dep[y])?x:y; }

void init_st(){
    for (int i=1; i<=n*2; ++i) st[i][0]=id[i];
    for (int i=1; i<logn; ++i)
        for (int j=1; j<=n*2; ++j) if (j+mi[i]-1<=n*2)
            st[j][i]=mindep(st[j][i-1], st[j+mi[i-1]][i-1]);
}

int log2(float x){
    return ((unsigned&)x>>23&255)-127;
}

int getlca(int x, int y){
    if (fir[x]>fir[y]) swap(x, y);
    int fx=fir[x], fy=fir[y];
    int logxy=log2(fy-fx+1);
    return mindep(st[fx][logxy],
                  st[fy-mi[logxy]+1][logxy]);
}

int dis(int x, int y){
    int lca=getlca(x, y);
    return dep[x]+dep[y]-2*dep[lca];
}

void dfs(int now, int par){
    Graph::Edge e=g.getlink(now);
    for (; *e; ++e) if (*e!=par) dfs(*e, now);
    e=g.getlink(now); best[now]=INF;
    for (int j=1; j<=n; ++j)
        dp[now][j]=(dis(now, j)<=d[now]?w[j]:INF);
    for (; *e; ++e) if (*e!=par)
        for (int j=1; j<=n; ++j) if (dis(now, j)<=d[now])
            dp[now][j]+=min(dp[*e][j]-w[j], best[*e]);
    for (int j=1; j<=n; ++j)
        best[now]=min(best[now], dp[now][j]);
}

int main(){
    scanf("%d", &T); int x, y, l;
    mi[0]=1; for (int i=1; i<logn; ++i) mi[i]=mi[i-1]*2;
    while (T--){
        g.reset(); scanf("%d", &n);
        for (int i=1; i<=n; ++i) scanf("%d", &w[i]);
        for (int i=1; i<=n; ++i) scanf("%d", &d[i]);
        for (int i=1; i<n; ++i){
            scanf("%d%d%d", &x, &y, &l);
            g.addedge(x, y, l); g.addedge(y, x, l);
        }
        time=0; predfs(1, 0); dep[1]=0;
        init_st(); dfs(1, 0);
        printf("%d\n", best[1]);
    }
    return 0;
}
时间: 2024-12-14 18:42:01

Fire (poj 2152 树形dp)的相关文章

poj 3342(树形dp)

题意:在一个公司中要举办一个聚会,每一个员工有一个奉献值.为了和谐规定直接上下级不能一起出席.让你找出奉献值之和最大为多少. 思路:dp[v][1]表示当前结点选,能获得的最大奉献值,dp[v][0]表示当前节点不选能获得的最大奉献值.状态转移: dp[v][0] = max(dp[v][0], ∑max(dp[x][1], dp[x][0]))x为直接儿子 dp[v][1] = max(dp[v][1], ∑dp[x][0] + vex[v]) 最后答案是max(dp[root][0], dp

poj 1947(树形dp)

题意:一棵树上问你最少切掉几条边使得能分割出一个结点数正好为k的子树. 思路:dp[i][j]表示以i为根切掉j个结点最少要几条边. dp[v][j] = min(dp[v][j], dp[v][j-k] + dp[x][k]); 代码如下: 1 dp[v][j] = min(dp[v][j], dp[v][j-k] + dp[x][k]); 2 } 3 } 4 } 5 } 6 } 7 return vex[v]; 8 } 9 10 int main() 11 { 12 // freopen("

POJ 2486 树形DP

有一颗苹果树,每个节点上面有很多苹果,从一个节点到另外一个可以到达的节点花费1步,求k步最多能吃到多少苹果,起始点为1,可以不回到起始点. 这是典型的回溯型树状dp. dp[i][j][0]代表以i为根节点的子树最多j步后回到i能吃到的最多的苹果, dp[i][j][1]代表以i为根节点的子树最多j步后不回到i节点最多能吃到的子树.那么状态转移就分三步了. (1)dp[i][j+2][0] = max(dp[i][j+2][0], dp[i][j-k][0]+dp[son][k][0]); (2

POJ 3342 树形DP入门题

题目意思和POJ2342一样,只是多加了一个条件,如果最大方案数唯一,输出Yes,不唯一输出No dp的是时候多加一个变量记录答案是否唯一即可 #include "stdio.h" #include "string.h" #include "vector" using namespace std; struct node { int fa; vector<int>child; }data[210]; struct comp { int

POJ 1947 树形DP入门题

给出N个点,N-1个关系,建出树形图,问最少减去几个边能得到节点数为P的树.典型树形DP题 dp[cur][j] :记录cur结点,要得到一棵j个节点的子树去掉的最少边数 转移方程用的背包的思想 对当前树的每一个子树进行计算 砍掉此子树:   dp[cur][j]=dp[cur][j]+1; 不砍掉:           for (l=0;l<=j;l++)  dp[cur][j]=Min(dp[cur][j],dp[cur][l]+dp[next][j-l]); 枚举从该树中留l个节点其他由新

POJ 2342 树形DP入门题

有一个大学的庆典晚会,想邀请一些在大学任职的人来参加,每个人有自己的搞笑值,但是现在遇到一个问题就是如果两个人之间有直接的上下级关系,那么他们中只能有一个来参加,求请来一部分人之后,搞笑值的最大是多少. 树形DP入门题. DP部分: dp[i][0]表示职员i不来参加party,以i为根的子树的最大搞笑值, dp[i][1]表示职员i来参加party,以i为根的子树的最大搞笑值. 转移方程: dp[cur][1]+=dp[next][0]; dp[cur][0]+=Max(dp[next][1]

poj 1655 树形dp求取树的重心

http://poj.org/problem?id=1655 Description Consider a tree T with N (1 <= N <= 20,000) nodes numbered 1...N. Deleting any node from the tree yields a forest: a collection of one or more trees. Define the balance of a node to be the size of the large

【poj2152】【Fire】【树形dp】

Fire Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 1161 Accepted: 595 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. Recentl

poj 2342(树形DP)

Anniversary party Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6062   Accepted: 3490 Description There is going to be a party to celebrate the 80-th Anniversary of the Ural State University. The University has a hierarchical structure