zoukankan      html  css  js  c++  java
  • 【POJ 2152】Fire

    题目大意

    给你一颗树,每个点都有一个 \(w\) 值,代表在这个点修建工程需要花费 \(w\),还有一个 \(d\) 值,代表离这个点距离小于 \(d\) 的点可以作为这个点的建立工程的点。每条边都有权重,求出覆盖所有点的最小花费。

    思路

    首先分析,是否具有最优子结构性质,发现任意一颗子树的最小值,都满足最优子结构。考虑动态规划的做法。

    这个题的难点在于可以选一个点来覆盖其他点,所以我们的一个想法必然是枚举这个被选取的点。

    \(dp_{u,j}\) 表示以 \(u\) 为根节点的子树,依赖于 \(j\) 节点(其中 \(u\) 节点一定依赖 \(j\) 节点,其他节点可以依赖别的)的最小值,\(ans_u\) 表示覆盖这颗子树的最小值。

    这两个状态之间的区别和联系是什么?其实 \(dp_{u,j}\) 是为了维护当前子树与它的父亲及以上节点的关系,而 \(ans_u\) 则是我们的普通动态规划设计的状态。

    所以这题的关键在于将 \(dp\)\(ans\) 转化。

    \(dp_{u,j}=\sum_{\text{edge}(u\rightarrow v)}(dp_{v,j}-w_j,ans_v)\),意思是,u点已经确定要选j点了,下面考虑它的子节点是否也选 \(j\) 点就行。

    最后用 \(dp_{u,j}\) 来更新 \(ans_u\) 即可。

    总结:这题和以前做的树形 DP 有很大不同,以前做的题目都是一次设计出状态方程就行,但这个因为它同时和子节点,父节点,以及和它相距不超过 \(d\) 的节点都有关系,所以,需要先设出 \(dp_{u,j}\) 来解决相距不超过 \(d\) 的问题,再以此来解决 \(ans_u\) 的问题。

    代码

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    const int N=1e4+5;
    struct edge {
        int to,nxt,w;
    } e[N<<1];
    int cnt=0,head[N],dis[N];
    void add(int u,int v,int w) {
        e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt,e[cnt].w=w;
    }
    void dfs1(int u,int fa) {
        for(int i=head[u]; i; i=e[i].nxt) {
            int v=e[i].w;
            if(v==fa) {
                continue;
            }
            dis[v]=dis[u]+e[i].w,dfs1(v,u);
        }
    }
    int n,best[N],f[N][N],d[N],w[N];
    void dfs2(int u,int fa) {
        for(int i=head[u]; i; i=e[i].nxt) {
            int v=e[i].w;
            if(v==fa) {
                continue;
            }
            dfs2(v,u);
        }
        dis[u]=0,dfs1(u,0),best[u]=1e9;
        for(int i=1; i<=n; i++) {
            f[u][i]=1e9;
        }
        for(int i=1; i<=n; i++) {
            if(dis[i]<=d[u]) {
                f[u][i]=w[i];
                for(int j=head[u]; j; j=e[j].nxt) {
                    int v=e[j].to;
                    if(v==fa) {
                        continue;
                    }
                    f[u][i]+=std::min(best[v],f[v][i]-w[i]);
                }
                best[u]=std::min(best[u],f[u][i]);
            }
        }
    }
    int main() {
        int T;
        scanf("%d",&T);
        while(T--) {
            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);
            }
            cnt=0,memset(head,0,sizeof(head));
            for(int i=1,x,y,z; i<n; i++) {
                scanf("%d %d %d",&x,&y,&z),add(x,y,z),add(y,x,z);
            }
            dfs2(1,0),printf("%d\n",best[1]);
        }
    }
    

    本文作者:AFewMoon,文章地址:https://www.cnblogs.com/AFewMoon/p/15489170.html

    知识共享许可协议

    本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。

    限于本人水平,如果文章有表述不当之处,还请不吝赐教。

  • 相关阅读:
    URLProtocol服务协议
    ODBC、OLEDB和ADO之间的关系 ,以及性能比较
    如何在VS2015查看C#界面窗体里的控件层次
    SpeechVoiceSpeakFlags枚举类型的详细解释
    SQL中遇到多条相同内容只取一条的最简单实现方法
    flink elasticsearch sink table 忽略部分字段开发
    flink elasticsearch source table 集成elasticsearch-hadoop connector开发
    记一次python 协程给合多线程死锁问题
    kubernetes gitlab runner java maven ci/cd 整体方案示例
    某云elasticsearch节点失效,手动重置primary,迁移分区
  • 原文地址:https://www.cnblogs.com/AFewMoon/p/15489170.html
Copyright © 2011-2022 走看看