UVa 1220——樹形dp
阿新 • • 發佈:2018-11-09
題目來源:https://vjudge.net/problem/UVA-1220
題目描述:紫書上樹形dp的第二個例題,在樹的最大獨立子集上加上了一個判斷唯一性。所以兩個同時dp即可,轉移方法如下:
對於最大獨立子集:
1.dp[u][1]表示選了u這個節點,則子節點不能選,dp[u][1]+=dp[v][0];
2.dp[u][0]表示沒選u這個節點,那麼子節點可選可不選,取其中最大的即可,dp[u][0]+=max(dp[v][1],dp[v][0])
對於唯一性判斷,用f[i][j]表示,f[u][0]=0表式唯一,f[u][1]表示不唯一,轉移有下兩個式子
1.當且僅當u的子節點f[v][0]=1全部成立時,f[u][1]才是1;
2.如果某個dp[v][0]==dp[v][1],則不唯一;如果max取到的對應的f為0,方案也不唯一,如dp[v][0]>dp[v][1],且f[v][0]=0
這樣,套用最大獨立子集的模板,加上f陣列判斷,就能實現遞推了,。
細節看程式碼:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> #include <string> #include <map> using namespace std; /* 注意f的轉移方式,有兩種: 1.當且僅當所有子節點的f[v][0]=0時,f[root][0]=0(這裡的0表示唯一) 2.如果出現子節點dp[v][0]==dp[v][1],則f[root][0]=0 */ const int maxn=205; vector<int>G[maxn]; int n; int dp[maxn][2];//表示選或不選 int f[maxn][2];//表示是否唯一 map<string,int>mp; string s1,s2; void init() { for(int i=0;i<maxn;i++)G[i].clear(); memset(dp,0,sizeof(dp)); memset(f,0,sizeof(f)); mp.clear(); } void dfs(int root) { if(G[root].size()==0) { dp[root][0]=0; dp[root][1]=1; return; } int size=G[root].size(); for(int i=0;i<size;i++) { int v=G[root][i]; dfs(v); if(f[v][0]==1)//子節點不唯一,父節點絕對不唯一 f[root][1]=1; dp[root][1]+=dp[v][0];//這一層選了,兒子層都不能選 if(dp[v][0]>dp[v][1]) { dp[root][0]+=dp[v][0]; if(f[v][0]==1) f[root][1]=1; } else { dp[root][0]+=dp[v][1]; if(dp[v][1]==dp[v][0]||f[v][1])//兩者相等也是不唯一的另一個條件 f[root][0]=1; } } dp[root][1]++; } int main() { while(scanf("%d",&n)!=EOF) { if(n==0)break; init(); cin>>s1; int top=0; mp[s1]=top++; for(int i=1;i<=n-1;i++) { cin>>s1>>s2; if(mp.find(s1)==mp.end()) mp[s1]=top++; if(mp.find(s2)==mp.end()) mp[s2]=top++; G[mp[s2]].push_back(mp[s1]); } dfs(0); if(dp[0][1]==dp[0][0]) { printf("%d No\n",dp[0][1]); } else if(dp[0][1]>dp[0][0]) { printf("%d ",dp[0][1]); if(f[0][1]) printf("No\n"); else printf("Yes\n"); } else { printf("%d ",dp[0][0]); if(f[0][0]) printf("No\n"); else printf("Yes\n"); } } return 0; }