树形dp入门6题

来源:互联网 发布:西门子840d宏程序编程 编辑:程序博客网 时间:2024/06/04 19:45

1、POJ-2342

在一个公司中,每个职员有一个快乐值ai,现在要开一个party,邀请了一个员工就不可能邀请其直属上司,同理邀请了一个人就不可以邀请其的直属员工,

问如何使得这个快乐值达到最大。

显然简单树形dp,对每个结点dp[i][0]表示不邀请这个员工,其子树达到的最大快乐值,dp[i][1]表示邀请i员工其子树达到的最大值。

dp[i][0]=(i的全部员工的max(dp[u][1],dp[u][0)相加,也就是其子员工来或不来的最大快乐值。

dp[i][1]=(i的全部员工的dp[u][0相加,也就是其子员工都不能不来的最大快乐值。

从大boss开始dp,最终结果就是max(dp[root][0],dp[root][1])


2、POJ-1463

现在要在一棵树上布置士兵,每个士兵在结点上,每个士兵可以守护其结点直接相连的全部边,问最少需要布置多少个士兵。

还是简单树形dp,状态转移方程好写。

dp[i][0]表示这个点不选(说明子结点全部选),因为只有子结点全部选,才能确保这个点到子结点的全部边都有士兵守卫。

dp[i][1]表示这个点选,那么子结点就是+min(选/不选),然后对根结点一样是判断0与1的大小即可

3、POS-2378

给一一颗树,问删除哪些结点可以使得剩下的子图的结点数<=总/2。

那么dp[i]表示这棵子树的下面的(包括其)的总结点数

如果一棵树其全部子树<=总/2,且总-其全部子树结点和-1<=总/2,那么这个结点就合适,用个数组来储存,最后排序即可

4、ZOJ-3201***(agin)

给一棵树,问这棵树大小为k的子树最大的权值和是多少。

这题稍微有点难度,dp[i][j]表示i结点长度为j(包括其)的最大权值是多少。

然后对于每个其子结点就进行背包dp,判断这个结点对于长度为l的权值是否应该要(这里需要注意的就是一个结点不能要多次,所以应该从大到小dp(类似背包),而且要一个数组储存最初状态,比如这个根结点只有长为1的链,但是这个子结点长为3,计算到2的时候这个根结点已经有长度为3的结点了,再计算子结点为3的时候,发现根结点有3长度的结点,就会出现这个根结点存在长度为6的链,显然这是错误的)。

5、POJ-3659***(agin)

类似上面第二题,这时一个士兵可以守护的为其相邻的全部结点,问最少需要布置的士兵数量。

可以这么写

dp[i][0]表示i结点不布置士兵,但是i结点的全部子结点被覆盖(有可能选了,但不影响,取最小即可)了(因为如果i的子结点存在不覆盖,而i又不选,显然无法满足题意)。

dp[i][1]表示i结点布置士兵

dp[i][2]表示i结点不布置士兵,但是其子结点存在布置了士兵的结点(表面i已经被覆盖)

dp[i][1]可以在每个子节点的0、1、2中选最小的推出

dp[i][0]可以在1、2中选

dp[i][2]可以在min(1,2)中选(至少选了一个1)

或者全部选2,再加上一个子结点1与2差值最小,意思还是得选一个1,但是是选差值最小那个,最后取根结点的1和2最小即可

6、HDU-2196***(agin)

给一棵树,有n个结点,结点之间的边有权值,问每个结点的最远结点距离其多远。

计算出每个结点到其全部直接连接的子结点的方向dfs下去可以达到最远距离

然后每个结点的最远距离只需要在其能连接到的子节点中找最远即可。

#include<cstdio>#include<cstring>#include <cmath>#include<algorithm>#include<vector>#include<iostream>using namespace std;typedef long long ll;struct ttt{int next,w,len;int next2;};vector<ttt>qq[10500];int dfs(int x,int y,int u){ //y为父结点,第几个    if(qq[x].size()==1&&y!=-1){ //即便其是1,也要得出其向后退的结果        //cout << y <<"到" <<x <<"长度为" << qq[y][u].w << endl;        qq[y][u].len=qq[y][u].w;    }else{        int i,j,k,t1,t2;        ttt v;        t1=0;        //cout << y <<"进入到" << x <<" " <<" "<<u<< endl;        for(i=0;i<qq[x].size();i++){ //记录这个结点到子的最远            v=qq[x][i];            if(v.next2==y)continue;            //cout << t1<<"从" << qq[x][i].len << "  中取得" <<endl;            t1=max(t1,qq[x][i].len);   //         t1=max(t1,qq[x][i].len);            if(qq[x][i].next==-1)continue;            qq[x][i].next=-1;         //   cout <<x <<"发现" << v.next << "进入" << endl;            dfs(v.next,x,i); //进入v结点,其父节点为x,位置是i            t1=max(t1,qq[x][i].len);            //这个结点的最大值为其所有子节点的最大值        }        if(u!=-1){                qq[y][u].len=+qq[y][u].w+t1;       // cout << y <<"到"<<x <<"的最大是"<< qq[y][u].len<<endl;        }    }}int max1;int dfs2(int x){    int i;    for(i=0;i<qq[x].size();i++)        max1=max(max1,qq[x][i].len);}int main(){    //freopen("in.txt","r",stdin);   // freopen("out1.txt","w",stdout);    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,n,m;    while(scanf("%d",&n)==1){        ttt u,v;        //for(i=1;i<=n;i++)          //  qq[i].clear();        memset(qq,0,sizeof(qq));        u.len=0;        for(i=2;i<=n;i++){            cin >> t1>> t2;            u.next2=u.next=t1;u.w=t2;            qq[i].push_back(u);            u.next2=u.next=i;            qq[t1].push_back(u);        }        for(i=1;i<=n;i++)        dfs(i,-1,-1);        for(i=1;i<=n;i++){         max1=-1e9;         dfs2(i);         cout << max1 <<endl;        }    }return 0;}





原创粉丝点击