poj 3342 树形DP

  近几天刚开始看树形DP,而这题算是一道经典题。

  题目大意就不赘述,总而言之就是一棵树上选子节点就不能选父节点,然后要选尽量多的节点出来。

  我们设dp[i][0]为以i为父节点时要加入这个节点,即选了这个boss,他的下属不能选,dp[i][1]为i为父节点时不能加入这个节点。

  所以有dp[i][0]=dp[soni][1]求和 然后再加上它本身 即在数值上加1

           dp[i][1]=max(dp[soni][1],dp[soni][0])求和       (soni为i的子节点)

  然而此题还有一个问题就是此解是否唯一,我被这个问题卡了好久,想用一些复杂的方法去搞它 但后来参考了tec12的方法。

  http://blog.csdn.net/tec12/archive/2010/08/10/5801741.aspx

  首先对于每个点都有两个选择方法,若不加入这个点,那么必然加入子节点,而又若子节点加于不加时的值相等,那么当然重复了,对于最大的Boss,他不依赖任何节点,所以要特殊判断一下。

 

代码较长,是用链表实现的

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<map>
using namespace std;

const int N=210;
int dp[N][2];
int n;

struct node
{
    int x;
    node *next;
}tree[N];

node *newnode()
{
    node *p=new node;
    p->next=NULL;
    return p;
}

void init()
{
    int i;
    for(i=1;i<=n;i++) tree[i].next=NULL;
    memset(dp,0,sizeof(dp));
}

void insert(int a,int b)
{
    node *p=&tree[a];
    while(p->next) p=p->next;
    node *q=newnode();
    q->x=b;
    p->next=q;
}

void dfs(int root)
{
    int temp1=0,temp2=0;
    node *p=tree[root].next;
    int son;
    while(p)
    {
        son=p->x;
        dfs(son);
        temp1+=dp[son][1];
        temp2+=max(dp[son][0],dp[son][1]);

        p=p->next;
    }
    dp[root][0]=temp1+1;
    dp[root][1]=temp2;
}

bool check()
{
    int i,son;
    node *p;
    for(i=1;i<=n;i++)
    {
        if(dp[i][1]>=dp[i][0])
        {
            p=tree[i].next;
            while(p)
            {
                son=p->x;
                if(dp[son][0]==dp[son][1]) return true;
                p=p->next;
            }
        }
    }
    return false;
}

int main()
{
    while(scanf("%d",&n)&&n)
    {
        string a,b;
        int i,na,nb;
        int t=1;
        cin>>a;
        init();
        map <string,int> m;
        m[a]=1;

        for(i=1;i<=n-1;i++)
        {
            cin>>a>>b;
            if(m.find(a)==m.end())
            {
                t++;
                m[a]=t;
            }
            na=m[a];
            if(m.find(b)==m.end())
            {
                t++;
                m[b]=t;
            }
            nb=m[b];
            insert(nb,na);
        }
        dfs(1);

        int ans=0;
        bool flag=false;
        ans=max(dp[1][0],dp[1][1]);
        flag=check();
        if(dp[1][0]==dp[1][1]) flag=true;
        printf("%d ",ans);
        if(!flag) printf("Yes/n");
        else printf("No/n");
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值