基本树形dp及例题

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string.h>
#define N 6005                            //树形dp POJ2342
using namespace std;
struct tree
{
	int child, father, brother;                      //结点的子节点,父亲结点, 兄弟结点
	int present, non_present;                       //present表示此人到场的最大价值, non_present表示此人不到场的最大价值
	void init()
	{
		child=father=brother=non_present=0;                //初始化
	}
}p[N];
void dfs(int x)
{
	int k=p[x].child;
	while(k)
	{
		dfs(k);      //深搜下去到子节点,从下到上
		p[x].present+=p[k].non_present;     //状态转移方程,x到场的话则加上它的孩子不到场的价值
		p[x].non_present+=max(p[k].present, p[k].non_present);//x不到场的话则加上他的孩子(到场或不到场---孩子和brother之间也不受影响)的最大值
		k=p[k].brother;     //x的孩子还包括(x的孩子的brother)
	}
	return ;
}
int main()
{
	int n, t, l, k;
	while(scanf("%d", &n)!=EOF)
	{
		for(t=1; t<=n; ++t)
		{
			scanf("%d", &p[t].present);
			p[t].init();
		}
		while(scanf("%d%d", &l, &k)!=EOF&&l+k!=0)
		{
			p[l].father=k;
			p[l].brother=p[k].child;   //一个接一个,记录上一个
			p[k].child=l;     //可以从这里逐渐往上推,推出k所有的child
		}
		for(t=1; t<=n; ++t)
		{
			if(!p[t].father)   //从根节点出发
			{
				dfs(t);     //搜索并记录下来
				printf("%d\n", max(p[t].present, p[t].non_present));
				break;
			}
		}
	}
	return 0;
}






#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <map>
#define LL long long 
#define inf 0x7fffffff
#define N 250
using namespace std;    //hdu2412  树形dp及最佳方案是否唯一
int dp[N][2], dup[N][2];     //dp[n][m]中m为0表示不来,m为1表示来, dup[n][m]表示最佳的方案是不是唯一的,dup[n][m]中m为0表示不唯一,m为1表示唯一
map<string, int> s;
/*
题意:n个人形成一个关系树,每个节点代表一个人,节点的根表示这个人的唯一的直接上司,只有根没有上司。
要求选取一部分人出来,使得每2个人之间不能有直接的上下级的关系,求最多能选多少个人出来,并且求出获得最大人数的选人方案是否唯一。
思路:树形dp
     dp[x][0]+=max(dp[k][0], dp[k][1]);   k是x的孩子
	 dp[x][1]+=dp[k][0];
*/
struct node
{
	int pre, brother, son;
	void init()
	{
		pre=brother=son=0;
		return ;
	}
}p[N];

void dfs(int x)
{
	int k=p[x].son;
	dup[x][0]=dup[x][1]=1;  //注意初始化每个人来和不来都是唯一的方案
	while(k)
	{
		dfs(k);
		dp[x][0]+=max(dp[k][0], dp[k][1]);
		dp[x][1]+=dp[k][0];
		if((dp[k][0]>dp[k][1]&&!dup[k][0])||(dp[k][0]<dp[k][1]&&!dup[k][1])||dp[k][0]==dp[k][1])
			dup[x][0]=0;
		if(!dup[k][0])
			dup[x][1]=0;
		k=p[k].brother;
	}
	return ;
}

int main()
{
	int n, t, j, k, id1, id2;
	string s1, s2;
	while(scanf("%d", &n)&&n)
	{
		s.clear();
		memset(dp, 0, sizeof(dp));
		for(t=1; t<=n; ++t)
		{
			p[t].init();
			dp[t][1]=1;
		}
		k=1;
		cin>>s1;
		s[s1]=k++;
		for(t=1; t<n; ++t)
		{
			cin>>s1>>s2;
			if(!s[s1])
			{
				s[s1]=k++;
			}
			if(!s[s2])
			{
				s[s2]=k++;
			}
			id1=s[s1];
			id2=s[s2];
			p[id1].pre=id2;
			p[id1].brother=p[id2].son;
			p[id2].son=id1;
		}
		dfs(1);
		int ans, flag=0;
		if(dp[1][0]>dp[1][1])
		{
			ans=dp[1][0];
			if(dup[1][0]==1)
				flag=1;
		}
		else if(dp[1][0]<dp[1][1])
		{
			ans=dp[1][1];
			if(dup[1][1]==1)
				flag=1;
		}
		else 
		{
			ans=dp[1][0];
		}
		printf("%d ", ans);
		if(flag)
		{
			printf("Yes\n");
		}
		else printf("No\n");
	}
	return 0;
}











#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <map>
#define LL long long 
#define inf 0x7fffffff
#define N 2500
using namespace std;     //hdu1054   树形dp
int dp[N][2];   
struct node
{
    int pre, brother, son;
    void init()
    {
        pre=brother=son=0;
        return ;
    }
}p[N];

void dfs(int x)
{
    int k=p[x].son;
    while(k)
    {
        dfs(k);
        dp[x][0]+=dp[k][1];
        dp[x][1]+=min(dp[k][0], dp[k][1]);
        k=p[k].brother;
    }
    return ;
}

int main()
{
    int n, t, a, k, b;
    while(scanf("%d", &n)!=EOF)
    {
        memset(dp, 0, sizeof(dp));
        for(t=1; t<=n; ++t)
        {
            p[t].init();
            dp[t][1]=1;
        }
        for(t=0; t<n; ++t)
        {
            scanf("%d:(%d)", &a, &k);   //注意这里编号可以从0开始,所以每一个编号都要加1
            a++;
            while(k--)
            {
                scanf("%d", &b);
                b++;
                p[b].pre=a;
                p[b].brother=p[a].son;
                p[a].son=b;
            }
        }
        int ans;
        for(t=1; t<=n; ++t)
        {
            if(!p[t].pre)
            {
                dfs(t);
                ans=min(dp[t][0], dp[t][1]);
                break;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}






#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <map>
#define LL long long
#define inf 0x7fffffff
#define N 10100
using namespace std;    //hdu 2196  树形dp
int vis[N], pre[N], dp[N][3];   //dp[n][m]中n是节点编号, m为0表示从下到上节点的最大值, m为1表示从小到上第2大值, m为2表示从上到下的最大值
/*
题意:给你一棵树,求树上各个节点到其他节点的距离中的最大值
思路:先用邻接表建树,树形dp
*/
struct node
{
	int v, next, w;   //表示的是一条边
}p[N<<1];
void init()
{
	memset(pre, -1, sizeof(pre));
	memset(dp, 0, sizeof(dp));
	memset(vis, 0, sizeof(vis));
}

void dfs1(int x)     //从下到上
{
	vis[x]=1;
	int j, t, biggest, bigger;
	biggest=bigger=0;
	for(j=pre[x]; j!=-1; j=p[j].next)
	{
		if(vis[p[j].v])    //之前访问过的祖先
			continue;
		dfs1(p[j].v);
		if(dp[p[j].v][0]+p[j].w>biggest)
		{
			bigger=biggest;
			biggest=dp[p[j].v][0]+p[j].w;
		}
		else if(dp[p[j].v][0]+p[j].w>bigger)
		{
			bigger=dp[p[j].v][0]+p[j].w;
		}
		dp[x][0]=biggest;
		dp[x][1]=bigger;
	}
	return ;
}

void dfs2(int x)   //从上到下
{
	vis[x]=1;
	int t, j, son;
	for(j=pre[x]; j!=-1; j=p[j].next)
	{
		son=p[j].v;
		if(vis[son])continue;
		dp[son][2]=(dp[son][0]+p[j].w==dp[x][0]?dp[x][1]:dp[x][0])+p[j].w;   //表示这个节点的父亲节点下的其他分支的最大值
		dp[son][2]=max(dp[son][2], dp[x][2]+p[j].w);   //父亲节点之上的最大值
		dfs2(p[j].v);
	}
}


int main()
{
	int n, t, j, k, a, b;
	while(~scanf("%d", &n))
	{

		k=0;
		init();
		for(t=2; t<=n; ++t)    
		{
			scanf("%d%d", &a, &b);   //无向图
			p[k].v=t;
			p[k].w=b;
			p[k].next=pre[a];
			pre[a]=k++;
			p[k].v=a;
			p[k].w=b;
			p[k].next=pre[t];
			pre[t]=k++;
		}
		dfs1(1);  //从下到上递推,求出dp[N][0]和dp[N][1]
		memset(vis, 0, sizeof(vis));
		dfs2(1);  //从上到下
		for(t=1; t<=n; ++t)
			printf("%d\n", max(dp[t][0], dp[t][2]));
	}
	return 0;
}








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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值