(树形dp入门)

版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/85121058

题目一: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;
}

猜你喜欢

转载自blog.csdn.net/LJD201724114126/article/details/85121058