树形dp

A - Anniversary party

这个题目是说,公司开宴会,为了让这个宴会的欢乐值尽量大,所以我们规定每一个员工的直属上司不能去,或者说每一个上司的直属员工不能去 , 然后求这个宴会的最大快乐值。

这个题目是我树形dp的入门题吧,虽然我到现在都还是不太懂树形dp,感觉就是模模糊糊的一个理解。

dp[i][0]:表示不邀请i员工其子树达到的最大快乐值,dp[i][1]则表示邀请。

其实我也不太懂为什么是这样定义的,不过暂时就这么想着吧。

这个需要建图,然后就是在树上进行dp。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <iostream>
using namespace std;
const int maxn = 6e3 + 10;
vector<int>G[maxn];
int a[maxn];
int f[maxn];
int dp[maxn][2];

void dfs(int u)
{
    for(int i=0;i<G[u].size();i++)
    {
        dfs(G[u][i]);
    }
    for(int i=0;i<G[u].size();i++)
    {
        dp[u][0] += max(dp[G[u][i]][0], dp[G[u][i]][1]);
        dp[u][1] += dp[G[u][i]][0];
    }
}

int main()
{
    int n;
    scanf("%d", &n);
    memset(f, -1, sizeof(f));
    for (int i = 1; i <= n; i++) scanf("%d", &dp[i][1]);
    int so, fa;
    while(scanf("%d%d",&so,&fa)==2&&(so+fa))
    {
        G[fa].push_back(so);
        f[so] = 1;
    }
    int root = 0;
    for(int i=1;i<=n;i++)
    {
        if (f[i] == -1) root = i;
    }
    dfs(root);
    printf("%d\n", max(dp[root][0], dp[root][1]));
    return 0;
}
A

 

B - Strategic game POJ - 1463 

这个题目和上面那个是一个意思,可以一样的进行dp,状态转移方程只是微变。

#include <cstdio>
#include <cstdlib>
#include <queue>
#include <vector>
#include <algorithm>
#include <iostream>
#include <cstring>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 10;
typedef long long ll;
int dp[maxn][10];
vector<int>G[maxn];
bool vis[maxn];

void dfs(int u)
{
    for(int i=0;i<G[u].size();i++)
    {
        dfs(G[u][i]);
    }
    for(int i=0;i<G[u].size();i++)
    {
        dp[u][0] += dp[G[u][i]][1];
        dp[u][1] += min(dp[G[u][i]][0], dp[G[u][i]][1]);
    }
}

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset(dp, 0, sizeof(dp));
        for (int i = 0; i <= n; i++) {
            G[i].clear();
            vis[i] = 0;
            dp[i][1] = 1;
        }
        for(int i=1;i<=n;i++)
        {
            int u, v, cnt;
            scanf("%d:(%d)", &u, &cnt);
            for(int j=1;j<=cnt;j++)
            {
                scanf("%d", &v);
                G[u].push_back(v);
                vis[v] = 1;
            }
        }
        int root = 0;
        for (int i = 0; i < n; i++) if (vis[i] == 0) {
            root = i;
            break;
        }
        dfs(root);
        printf("%d\n", min(dp[root][1], dp[root][0]));
    }
    return 0;
}
B

 

C - Tree Cutting POJ - 2378 

题目大意:给一颗n个结点的树,节点编号为1~n,问删除一个节点之后,让剩下的分支中节点数量最多的尽量少。可能有多种方案,按编号顺序输出。

这个题目说是一个树形dp,但是我感觉更像是一个搜索的思维题,

这个题目说是要删去一部分节点,使得之后形成的联通块的所有节点小于等于n/2

这个我们可以把任何一个点当作是根节点,所以我们只要求出这个根节点的子树的节点最大也要小于等于n/2就是满足条件的节点。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e4 + 10;
int dp[maxn], num[maxn];
vector<int>G[maxn];
int n;

int dfs1(int u,int pre)
{
    int sum = 1;
    for(int i=0;i<G[u].size();i++)
    {
        if (G[u][i] == pre) continue;
        int res=dfs1(G[u][i], u);
        sum += res;
        num[u] = max(num[u], res);
    }
    num[u] = max(num[u], n - sum);
    return sum;
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n-1;i++)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs1(1, -1);
        for(int i=1;i<=n;i++)
        {
            if (num[i] <= n / 2) printf("%d\n", i);
        }
    }
    return 0;
}
C

 

posted @ 2019-06-05 10:25  EchoZQN  阅读(104)  评论(0编辑  收藏  举报