poj2342/poj3342——基础的树形DP

poj2342:

题目大意:

大学要开年会,请公司的人来参加聚会,但是不希望员工和其最直接的上司同时出现在聚会上,问最多能邀请到多少人参加聚会。


解题思路:

转化成树的结构就是父亲节点和孩子节点不能同时出现。

设dp[i][0] 表示不取第i个人能得到的最大人数。

    dp[i][1] 表示取第i个人能得到的最大人数。

可以得到如下的状态转移方程:

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

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

其中j是i的孩子节点。

所以我们只需要对根节点进行一次dfs求出所有的dp数组所有的值。

最后结果就是 max(dp[root][1] ,dp[root][0])


poj3342是上题的升级版本,除了求出最多人数之外,还需要判断最多人数的方案是否唯一。

方案唯一性的判断方法:

第1:如果dp[root][1]==dp[root][0],则方案必不唯一。

第2:如果dp[i][1]==dp[i][0],并且dp[father][0]>=dp[father][1]。

如果存在某节点其取与不取最大值是一样的,并且其父节点在不取的时候得到最大人数。

那么,这个时候方案不唯一。


poj3342源代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<algorithm>
#include<iostream>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const double eps=1e-8;
int n;
int dp[205][2];
int flag;
struct node
{
    int father;
    vector<int> child;
}p[205];
int vis[205];
void dfs(int now)
{
    int flag1;
    int i,size,child;
    vis[now]=1;
    size=p[now].child.size();
    flag1=0;
    for(i=0;i<size;i++)
    {
        child=p[now].child[i];
        if(!vis[child])
        {
            dfs(child);
            dp[now][0]+=max(dp[child][0],dp[child][1]);
            dp[now][1]+=dp[child][0];
        }
    }
    return;
}
int main()
{
	freopen("in.txt","r",stdin);
	int i,x,y,cnt,j;
	char ch[105];
	string a,b;
	while(scanf("%d",&n)==1 && n)
	{
        map<string,int> m;
	    memset(p,0,sizeof(p));
	    cin>>a;
	    m[a]=1;
	    cnt=1;
	    for(i=1;i<n;i++)
	    {
	        cin>>a>>b;
	        x=m[a],y=m[b];
	        if(m[a]==0)
	        {
	            m[a]=++cnt;
	            x=cnt;
	        }
	        if(m[b]==0)
	        {
	            m[b]=++cnt;
	            y=cnt;
	        }
	        //x和y已经是映射好的值了,x是y的下属
	        p[y].child.push_back(x);
	        p[x].father=y;
	    }
	    memset(dp,0,sizeof(dp));
	    for(i=1;i<=n;i++)
            dp[i][1]=1;
        memset(vis,0,sizeof(vis));
        dfs(1);
        printf("%d ",max(dp[1][1],dp[1][0]));


        //唯一性的判断,当前节点取和不取结果是一样的,
        //并且其父亲节点不取,这样的话必然出现多种可能的选择
        flag=1;
        if(dp[1][1]==dp[1][0])  flag=0;
        for(i=2;i<=n;i++)
        {
            if(dp[i][1]==dp[i][0] && dp[p[i].father][0]>=dp[p[i].father][1])
            {
                flag=0;
                break;
            }
        }

        if(flag)    printf("Yes\n");    //是独一无二的
        else        printf("No\n");     //不是独一无二的
        m.clear();                      //用完之后将m清空
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值