NKOJ 1791 Party at Hali-Bula(树状DP)

题目大意是某些人将去参加一次party 但是他们和他们不能和他们的上司一起参加,问满足此条件的情况下能去的最多人数

首先由给出的条件建好树,我是用邻接矩阵来存的,主要是为了方便,实际上效率还是比较低的。

需要保存两个状态 一是某节点不用(即此人不去的情况)时该节点和该节点子树的最大值dp[i][0];

                       二是是用该节点时,该节点及其子树的最大值dp[i][1];

 

对于dp[i][1] 状态转移方程比较明显 dp[i][1]=1+∑(dp[j][0])j是i的“孩子”;因为 i 去,则他的孩子就不能去了,所以此种情况下,能去的最大人数就是在他所有孩子不去的情况下的最大人数加他自己了。

 

dp[i][0]:由于i不去,i的孩子j也可去可不去 此时状态转移方程为: dp[i][0]=∑(max(dp[i][0],dp[i][1]))

 

还需判断去的人数最多的情况,这些人是不是固定的。

 

对于叶子节点 pan[j][0]=1;pan[j][1]=1;表示人数是确定的。

 

对于任意节点i dp[i][1] 固定即pan[i][1]=1当且仅当 i 的所有孩子 j pan[j][0]=1;

 

dp[i][0] :

对于任意一个孩子 j 如果有 dp[ij[0]>dp[j][1]&&pan[j][0]==0或者 dp[j][0]<dp[j][1]&&pan[j][1]==0 或者dp[j][0]==dp[j][1]

                 pan[i][0]=0;

 

即,构成dp[i][0]的数据中,如果有一个是不确定的,那么dp[i][0]就是不确定的

 

我用的是记忆化搜索来实现DP的

代码:

 

#include <iostream>

#include <cstring>

#include <cstdlib>

#include <stdio.h>

#include <map>

#include <algorithm>

using namespace std;

 

int n;

bool rela[210][210];

bool pan[210][2];

int dp[210][2];

 

int main()

{

    while (cin>>n&&n)

    {

        memset(rela,0,sizeof(rela));

        memset(pan,1,sizeof(pan));

 

     for(int i=0;i<=n;++i){

 

         dp[i][0]=dp[i][1]=-1;

         }

 

        map<string,int> myMap;

        int i(1);

        string a,b;

        cin>>a;

        myMap[a]=i;

        int j(1);

        while (j<n)

        {

            cin>>a;

            cin>>b;

            if (!myMap.count(a)) myMap[a]=++i;

            if (!myMap.count(b)) myMap[b]=++i;

 

            rela[myMap[b]][myMap[a]]=1;

 

            ++j;

        }

/*

        j=1;

        while(j<=n){

 

            int k(1);

            while(k<=n){

                cout<<rela[j][k]<<" ";

                ++k;

                }

            cout<<endl;

            ++j;

            }

*/

        void dfs(int n);

        j=1;

        while (j<=n)

        {

            dfs(j);

            ++j;

        }

 

        bool mul(0);

        int maxOut=max(dp[1][1],dp[1][0]);

        if(dp[1][1]>dp[1][0]&&!pan[1][1]) mul=1;

        if(dp[1][1]<dp[1][0]&&!pan[1][0]) mul=1;

        if(dp[1][1]==dp[1][0]) mul=1;

 

 

        cout<<maxOut<<" ";

        if (mul) cout<<"No"<<endl;

        else cout<<"Yes"<<endl;

    }

 

    return 0;

}

 

void dfs(int num)

{

 

 

    int j(1);

    int tempA(0);

    bool mulA(0);

    int tempB(0);

    bool mulB(0);

    bool isLeaf(1);

    while (j<=n)

    {

        if (rela[num][j])

        {

            isLeaf=0;

 

            if (dp[j][0]==-1)

                dfs(j);

 

            if (!pan[j][0]) mulB=1;

 

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

            if ((dp[j][1]>dp[j][0]&&!pan[j][1])||(dp[j][1]<dp[j][0]&&!pan[j][0])||(dp[j][1]==dp[j][0]))

               mulA=1;

            tempB+=dp[j][0];

 

        }

 

        ++j;

    }

    if (isLeaf)

    {

        dp[num][0]=0;

        dp[num][1]=1;

        return;

    }

 

    dp[num][0]=tempA;

    dp[num][1]=tempB+1;

 

 

    if (mulA) pan[num][0]=0;

    if (mulB) pan[num][1]=0;

    return;

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值