1. 程式人生 > 其它 >【POJ 2152】Fire

【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 國際許可協議 進行許可。

限於本人水平,如果文章有表述不當之處,還請不吝賜教。