BZOJ 4557 JLOI2016 侦查守卫 树形dp
題目鏈接:https://www.lydsy.com/JudgeOnline/problem.php?id=4557
?
題意概述:
給出一棵樹,每個(gè)點(diǎn)付出代價(jià)w[i]可以控制距離和它不超過(guò)d的點(diǎn),現(xiàn)在給出一些點(diǎn),問(wèn)控制這些點(diǎn)的最小代價(jià)是多少。
?
分析:
觀察一下數(shù)據(jù)范圍發(fā)現(xiàn)算算法的復(fù)雜度可能和d有關(guān)。橫看豎看這像是一個(gè)樹形dp,所以我們就把d搞到狀態(tài)方程里面去嘛怎么就完全沒(méi)有想到呢......
既然要用樹形dp,就要先分析一下性質(zhì)。
一個(gè)點(diǎn)如果被選擇成為控制點(diǎn),那么它可以控制的點(diǎn)有:子樹中深度不超過(guò)d的點(diǎn),祖先中和它距離不超過(guò)d的點(diǎn),以及祖先的子樹中的一些點(diǎn)。
感覺(jué)很麻煩的樣子......因?yàn)閷?duì)于那些祖先子樹中的點(diǎn)控制的方向突然向上又向下了。
我們考慮到常用的技巧,在樹形dp中,如果兩個(gè)點(diǎn)會(huì)對(duì)答案產(chǎn)生貢獻(xiàn),我們?cè)谄銵CA處統(tǒng)計(jì)貢獻(xiàn)。于是我們?cè)O(shè)兩個(gè)dp方程:
f(i,x)表示i點(diǎn)的子樹中需要被控制的點(diǎn)全部被控制,還可以向上控制x層的最小代價(jià);g(i,x)表示i點(diǎn)的子樹中x層及以下需要被控制的點(diǎn)全部被控制的最小代價(jià)。
需要向上控制x層,那么兒子中就需要有點(diǎn)可以向上控制x +1層的點(diǎn)被選擇,對(duì)于新來(lái)的子樹j有兩種情況,一個(gè)是我們需要的點(diǎn)在這個(gè)新的子樹中,一個(gè)是我們需要的點(diǎn)在原來(lái)的子樹中。
f(i,x)=min(f(i,x)+g(j,x),f(j,x+1)+g(i,x+1))
init:一開(kāi)始把每點(diǎn)i當(dāng)成孤點(diǎn),那么向上控制1~d層就只有靠自己,f值初始化為w[i];f[i][0],g[i][0]根據(jù)這個(gè)點(diǎn)本身是否需要監(jiān)視來(lái)判斷。
但是注意答案在控制的長(zhǎng)度恰好為x的時(shí)候不一定是最優(yōu)的,可能稍微控制的長(zhǎng)度大一點(diǎn)答案反而更優(yōu),于是把方程的意義改一下,改成至少控制x層。
g(i,x)=sum{g(j,x-1)|i->j},g(i,0)=f(i,0)
小技巧:怎么維護(hù)至少這個(gè)性質(zhì)?和看起來(lái)更劣的狀態(tài)取min即可。
?
?
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<set> 9 #include<map> 10 #include<vector> 11 #include<cctype> 12 #define inf 1e9 13 using namespace std; 14 const int maxn=500005; 15 const int maxd=25; 16 17 int N,D,M,W[maxn]; 18 struct edge{ int to,next; }E[maxn<<1]; 19 int first[maxn],np,f[maxn][maxd],g[maxn][maxd]; 20 bool ob[maxn]; 21 22 void _scanf(int &x) 23 { 24 x=0; 25 char ch=getchar(); 26 while(ch<'0'||ch>'9') ch=getchar(); 27 while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); 28 } 29 void add_edge(int u,int v) 30 { 31 E[++np]=(edge){v,first[u]}; 32 first[u]=np; 33 } 34 void data_in() 35 { 36 _scanf(N);_scanf(D); 37 for(int i=1;i<=N;i++) _scanf(W[i]); 38 _scanf(M); 39 int x,y; 40 for(int i=1;i<=M;i++){ 41 _scanf(x); ob[x]=1; 42 } 43 for(int i=1;i<N;i++){ 44 _scanf(x);_scanf(y); 45 add_edge(x,y); add_edge(y,x); 46 } 47 } 48 void DFS(int i,int fa) 49 { 50 for(int d=1;d<=D;d++) f[i][d]=W[i]; 51 f[i][D+1]=inf; 52 if(ob[i]) f[i][0]=g[i][0]=W[i]; 53 for(int p=first[i];p;p=E[p].next){ 54 int j=E[p].to; 55 if(j==fa) continue; 56 DFS(j,i); 57 for(int d=0;d<=D;d++) 58 f[i][d]=min(f[i][d]+g[j][d],f[j][d+1]+g[i][d+1]); 59 for(int d=D;d>=0;d--) f[i][d]=min(f[i][d],f[i][d+1]); 60 g[i][0]=f[i][0]; 61 for(int d=1;d<=D;d++) g[i][d]+=g[j][d-1]; 62 for(int d=1;d<=D;d++) g[i][d]=min(g[i][d],g[i][d-1]); 63 } 64 } 65 void work() 66 { 67 DFS(1,0); 68 printf("%d\n",f[1][0]); 69 } 70 int main() 71 { 72 data_in(); 73 work(); 74 return 0; 75 } View Code?
轉(zhuǎn)載于:https://www.cnblogs.com/KKKorange/p/8678650.html
總結(jié)
以上是生活随笔為你收集整理的BZOJ 4557 JLOI2016 侦查守卫 树形dp的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: iOS 14 如何将来电设置为全屏幕或横
- 下一篇: 在spring中该如何使用DTO,以及D