1. 程式人生 > >UVa 1220——樹形dp

UVa 1220——樹形dp

題目來源: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;
}