【51nod1299】监狱逃离(树形DP)
點此看題面
大致題意: 在一棵樹中有NNN條邊連接N+1N+1N+1個節點,現在已知這棵樹上的MMM個節點,要求封住最少的節點,使這MMM個節點中的任意一個節點無法到達葉子節點,若能辦到輸出最少封住的節點數,若不能輸出?1-1?1。
這道題目的正解是樹形DPDPDP(hl666hl666hl666大佬說用O(n2m)O(n^2m)O(n2m)的最小割也可以過這道題,不過這篇博客并不討論這種做法,他能做出來是因為他是常數之神)。
樹形DPDPDP,一般都將狀態由子節點向父節點轉移,這題當然也不例外。
我們可以用fff數組來存儲每一個節點的狀態:
若f[i]=0f[i]=0f[i]=0,則說明該節點已被完全封死,相當于既不可能有犯人到達這里,也不可能從這里到達出口。
若f[i]=1f[i]=1f[i]=1,則說明可以從這個節點到達葉子節點。
若f[i]=2f[i]=2f[i]=2,則說明有犯人可以到達該節點。
每一次轉移,我們可以用一個s[]數組來統計當前節點的子節點中f[son[i]]f[son[i]]f[son[i]]分別為0,1,20,1,20,1,2的次數,并依次進行分類討論:
若當前節點有犯人,則需要在該節點的子節點中的每個可以到達葉子節點的節點上安排一個警衛。且f[x]=2f[x]=2f[x]=2(當前節點上有犯人,所以犯人可以到達該節點)。
若既有犯人可以到達該節點的某個子節點,又可以通過該節點的某個子節點到達葉子節點,這說明有犯人可以通過該節點逃出監獄,則需在這個節點安排一個警衛。且f[x]=0f[x]=0f[x]=0(當前節點安排了警衛,所以被完全封死)。
否則,如果該節點的某個子節點可以到達葉子節點,則說明該節點也可以到達葉子節點,因此f[x]=1f[x]=1f[x]=1。
否則,如果有犯人可以到達該節點的某個子節點,則說明犯人也可以到達該節點,因此f[x]=2f[x]=2f[x]=2。
如果以上情況皆不滿足,這說明該節點是葉節點或其子節點全被封死,則特判其為葉子節點的情況,若其不是葉子節點則f[x]=0f[x]=0f[x]=0。
既然這樣,就很容易進行動態規劃了,具體代碼如下:
代碼
#include<bits/stdc++.h> #define N 100000 using namespace std; int n,m,ee=0,ans,a[N+5]={0},lnk[N+5]={0},OUT[N+5]={0},f[N+5]={0}; struct edge {int to,nxt; }e[2*N+5]; inline char tc() {static char ff[100000],*A=ff,*B=ff;return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) {x=0;int f=1;char ch;while(!isdigit(ch=tc())) if(ch=='-') f=-1;while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));x*=f; } inline void write(int x) {if(x<0) putchar('-'),x=-x;if(x>9) write(x/10);putchar(x%10+'0'); } inline void add(int x,int y) {e[++ee]=(edge){y,lnk[x]},lnk[x]=ee,++OUT[x]; } inline void dp(int x,int lst)//具體的DP過程 {int s[3];s[0]=s[1]=s[2]=0;//用s[]數組來統計當前節點子節點中各種狀態的次數register int i;for(i=lnk[x];i;i=e[i].nxt) if(e[i].to^lst) dp(e[i].to,x),++s[f[e[i].to]];//先將其子節點進行DP,是一個遞歸的過程//接下來是分類討論的過程if(a[x]) ans+=s[1],f[x]=2;//若當前節點有犯人else if(s[1]&&s[2]) ans++,f[x]=0;//若當前節點既能到達,又能出去else if(s[1]) f[x]=1;//若當前節點能出去else if(s[2]) f[x]=2;//若當前節點能到達else if(s[0]) f[x]=0;//若以上情況皆不滿足且該節點不是葉節點 } int main() {register int i;int x,y,fst=-1;for(read(n),read(m),i=1;i<=n;++i) read(x),read(y),add(x,y),add(y,x);for(i=1;i<=m;++i) {read(x),a[x]=1;if(OUT[x]==1) return puts("-1"),0;//若有葉子節點關押犯人,則該犯人能直接逃脫}for(i=1;i<=n;++i) if(!(OUT[i]^1)) (fst==-1?fst=i:0),f[i]=1;//將葉子節點的狀態初始化為1 dp(fst,0);//從一個葉子節點開始DPreturn write(f[fst]==2?ans+1:ans),0;//特判該節點為2,即犯人可以到達的情況,由于當前節點是葉子節點,所以要在當前節點在安排一個警衛,答案加1 }總結
以上是生活随笔為你收集整理的【51nod1299】监狱逃离(树形DP)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 51nod 1299 监狱逃离 树形D
- 下一篇: 51nod1299 监狱逃离 最小割