1. 程式人生 > >(樹形dp入門)

(樹形dp入門)

題目一:Anniversary party 

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=1520

題意:有n個客人,每個客人有一個開心值,然後客人K是客人L的主管,客人K和客人L不能一起被邀請參加party,問:邀請人的最大開心值是多少?

題解:顯然這是道樹形dp 的題,我們定義 dp[i][1]表示邀請客人i,dp[i][0]表示不邀請客人i,

故轉移方程為:

dp[i][1]+=d[j][0] ,

dp[i][0]+=max(dp[j][1],dp[j][0])。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>

using namespace std;

const int maxn=6010;

int dp[maxn][2];
vector<int> G[maxn];
int fa[maxn];

int dfs(int root)
{
    int len=G[root].size();
    for(int i=0;i<len;i++) ///一直遞迴到葉子節點,返回上一層開始
        dfs(G[root][i]);

    for(int i=0;i<len;i++)
    {
        dp[root][1]+=dp[G[root][i]][0];
        dp[root][0]+=max(dp[G[root][i]][0],dp[G[root][i]][1]);
    }
}

int main()
{
    int n;

    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&dp[i][1]); ///表示邀請客人i的開心值
            dp[i][0]=0;
            G[i].clear();
            fa[i]=-1;
        }

        int L,K;
        while(scanf("%d%d",&L,&K))
        {
            if(L==0&&K==0) break;
            
            ///在這裡我們就不建立雙向邊了,因為我們是從最深一層開始往上dp,故每個節點都會只搜一次
            G[K].push_back(L);
            fa[L]=K; ///

        }

        int root=1; ///找到根節點
        while(fa[root]!=-1) root=fa[root];

        dfs(root);

        printf("%d\n",max(dp[root][1],dp[root][0]));
    }
    return 0;
}

 

題目二:Strategic Game

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=1054

題意:給你一個n個節點的樹,讓你挑選出一些節點放士兵,使得所有邊都能被士兵監視,讓你儘可能的少放士兵。

題解:

顯然這道題跟上一道類似,也可以用樹形dp來解決,還是一樣dp[i][1]表示挑選節點i放士兵,dp[i][0]表示不挑選節點i放士兵,

轉移方程為:

dp[i][0]+=d[j][1] ,

dp[i][1]+=min(dp[j][1],dp[j][0])。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>

using namespace std;

const int maxn=1510;

int dp[maxn][2];
vector<int> G[maxn];
int fa[maxn];

int dfs(int root)
{
//    printf("f");
    int len=G[root].size();
    for(int i=0;i<len;i++) ///一直遞迴到葉子節點,返回上一層開始
        dfs(G[root][i]);

    for(int i=0;i<len;i++)
    {
        dp[root][0]+=dp[G[root][i]][1];
        dp[root][1]+=min(dp[G[root][i]][0],dp[G[root][i]][1]);
    }

}

int main()
{
    int n;

    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++)
        {
            dp[i][1]=1;///表示邀請客人i的開心值
            dp[i][0]=0;
            G[i].clear();
            fa[i]=-1;
        }

       int root,rootlen,son;
       for(int i=0;i<n;i++)
        {
            scanf("%d:(%d)",&root,&rootlen);

            while(rootlen--){

            scanf("%d",&son);
            ///在這裡我們就不建立雙向邊了,因為我們是從最深一層開始往上dp,故每個節點都會只搜一次
            G[root].push_back(son);
            fa[son]=root;

            }

        }


         root=0; ///找到根節點
        while(fa[root]!=-1) root=fa[root];
// printf("f");
        dfs(root);

        printf("%d\n",min(dp[root][1],dp[root][0]));
    }
    return 0;
}