• poj2152 Fire(树形DP)


    题目链接:https://vjudge.net/problem/POJ-2152

    题意:给定一颗大小为n的树,在每个结点建消防站花费为w[i],如果某结点没有消防站,只要在它距离<=d[i]的结点有消防站即可,求最小花费。

    思路:

      好难的树形dp,一点思绪也木有,只能搜题解。

      用dp[u][i]表示以u为根的子树满足条件,并且结点u依赖于结点i的最小花费。用best[u]表示以u根的子树满足条件的最小花费,那么best[u]=min(dp[u][i])。

      求best[u]时,先跑一遍dfs得到所有结点距离u的距离dis[i]。如果dis[i]>d[u],那么u没法依赖i,此时dp[u][i]=inf。否则dis[i]<=d[u],此时dp[u][i]=w[i]+sum( min( best[v] , dp[v][i]-w[i] ) ),其中i从1遍历到n,v是u的子结点。因为v的依赖有两种情况,如果v依赖于以v为根的子树中的结点,即best[v]; 如果v依赖于其余的结点,那么一定是i。反证一下,如果v依赖于k,那么u也一定依赖于k。所以应取best[v]和dp[v][i]-w[i]的最小值,减w[i]是因为w[i]多加了一次。

    AC代码:

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    const int maxn=1e3+5;
    const int inf=0x3f3f3f3f;
    int T,n,cnt,head[maxn],w[maxn],d[maxn],dp[maxn][maxn],dis[maxn];
    int best[maxn];
    
    struct node{
        int v,w,nex;
    }edge[maxn<<1];
    
    void adde(int u,int v,int w){
        edge[++cnt].v=v;
        edge[cnt].w=w;
        edge[cnt].nex=head[u];
        head[u]=cnt;
    }
    
    void getdis(int u,int fa,int len){
        dis[u]=len;
        for(int i=head[u];i;i=edge[i].nex){
            int v=edge[i].v;
            if(v==fa) continue;
            getdis(v,u,len+edge[i].w);
        }
    }
    
    void dfs(int u,int fa){
        for(int i=head[u];i;i=edge[i].nex){
            int v=edge[i].v;
            if(v==fa) continue;
            dfs(v,u);
        }
        getdis(u,0,0);
        best[u]=inf;
        for(int i=1;i<=n;++i){
            if(dis[i]>d[u]) dp[u][i]=inf;
            else{
                dp[u][i]=w[i];
                for(int j=head[u];j;j=edge[j].nex){
                    int v=edge[j].v;
                    if(v==fa) continue;
                    dp[u][i]+=min(best[v],dp[v][i]-w[i]);
                }
            }
            best[u]=min(best[u],dp[u][i]);
        }
    }
    
    int main(){
        scanf("%d",&T);
        while(T--){
            scanf("%d",&n);
            cnt=0;
            for(int i=1;i<=n;++i)
                head[i]=0;
            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){
                int u,v,w;
                scanf("%d%d%d",&u,&v,&w);
                adde(u,v,w);
                adde(v,u,w);
            }
            dfs(1,0);
            printf("%d
    ",best[1]);
        }
        return 0;
    }
  • 相关阅读:
    题解 P5996 【[PA2014]Muzeum】
    题解 CF1433G 【Reducing Delivery Cost】
    题解 CF1430E 【String Reversal】
    题解 CF710F 【String Set Queries】
    题解 P4334 【[COI2007] Policija】
    LIS 树状数组优化
    离散化模板
    P4309 [TJOI2013]最长上升子序列
    p3902 递增(incr)
    poj3417 暗的连锁
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/11419331.html
Copyright © 2020-2023  润新知