UVA1220树上的dp(最大独立集)

4 篇文章 0 订阅

这题做的真是心力憔悴,主要还是对dfs的不熟悉吧。

主要思路:对于每一个节点,有选择和不选择两种情况。

如果是不选择,则它的儿子可以是不选择和选择,取最大值,即 ∑max(d[v][0],d[v][1]);(v为u的子节点);

如果选择,则它的儿子不能选择即,∑d[v][0]+1(加1为加上父节点)。

然后最后求最大值。

不过这题还有一个地方就在于要判断是不是唯一路径。所以 还需要加一个judge[u][i]来做判断。(judge[u][0]==true表示不拿该节点,路径不唯一。为false则表示唯一)。(这个思路非常重要)

这个题还有一个总结出来的小技巧:用map对节点名是字符串进行编号,这样放入vector就方便很多了。


递归版本(效率低很多,但也能AC。。不推荐):

#include<cstdio>
#include<map>
#include<string>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=300;
string temp,namef,names,out;///用一个map存储该节点的数字代号,v[i][j]表示第i个节点的第j个儿子
int n,cnt,judge[maxn][2];
int dp(int st,int t,vector<int> edge[]){
    if(!edge[st].size()){
         if(t==0)
            return 0;
        else
            return 1;
    }
    int ans=0;
    if(t==1){
    for(int i=0;i<edge[st].size();i++){
        ans+=dp(edge[st][i],0,edge);
        if(judge[edge[st][i]][0])
            judge[st][1]=true;
    }
        return ans+1;
    }
    if(t==0){
    for(int i=0;i<edge[st].size();i++){
        int m1=dp(edge[st][i],0,edge);
        int m2=dp(edge[st][i],1,edge);
        if(m1>m2){
            if(judge[edge[st][i]][0])
                judge[st][0]=true;
            ans+=m1;
        }
        else if(m2>m1){
            if(judge[edge[st][i]][1])
                judge[st][0]=true;
            ans+=m2;
        }
        else if(m1==m2){
            judge[st][0]=true;
            ans+=m1;
        }
        //ans+=max(m1,m2);
    }
        return ans;
    }
}

int main(){
 //freopen("out.txt", "w", stdout);//参数分别是,输出的文件的文件名,w是write(写),stdout(输出),
    while(scanf("%d",&n)!=EOF){
        if(n==0)
             break;
        map<string,int> id;
        vector<int> edge[maxn];
        memset(judge,0,sizeof(judge));
        cnt=1;
        cin>>temp;
        for(int i=0;i<n-1;i++){
            cin>>names;
            cin>>namef;
            if(!(id[namef]>=1&&id[namef]<=n)){
               id[namef]=cnt++;
            }
            if(!(id[names]>=1&&id[names]<=n)){
               id[names]=cnt++;
            }
            int u=id[namef];
            int v=id[names];
            edge[u].push_back(v);
        }
        //
        int st=id[temp];
        int ans1=dp(st,0,edge);
        int ans2=dp(st,1,edge);
        if(ans1 == ans2)  printf("%d No\n", ans1);//判断谁在数更大
        else if(ans1 > ans2)  printf("%d %s\n", ans1, judge[st][0] ? "No" : "Yes");
        else  printf("%d %s\n", ans2, judge[st][1] ? "No" : "Yes");
    }
    return 0;
}


记忆化搜索(极力推荐)树结构存图用vector

#include<cstdio>
#include<map>
#include<string>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=300;
string temp,namef,names,out;///用一个map存储该节点的数字代号,v[i][j]表示第i个节点的第j个儿子
int n,cnt,judge[maxn][2],d[maxn][maxn];

void dfs(int st,vector<int> edge[]){
    if(!edge[st].size()){///走到最后的叶子节点
        d[st][1]=1;
        d[st][0]=0;
        return;
    }
    for(int i=0;i<edge[st].size();i++){
        int son=edge[st][i];
        dfs(son,edge);
        d[st][1]+=d[son][0];
        if(judge[son][0])
            judge[st][1]=true;///拿了父节点,没拿子节点的情况

        if(d[son][1]>d[son][0]){///不拿父节点,子节点随意
            d[st][0]+=d[son][1];
            if(judge[son][1])
                judge[st][0]=true;
        }
        else if(d[son][1]<d[son][0]){
            d[st][0]+=d[son][0];
            if(judge[son][0])
                judge[st][0]=true;
        }
        else{
            judge[st][0]=true;
            d[st][0]+=d[son][1];
        }
    }
    d[st][1]++;///遍历完所有子节点要记得加上1
}

int main(){
 //freopen("out.txt", "w", stdout);//参数分别是,输出的文件的文件名,w是write(写),stdout(输出),
    while(scanf("%d",&n)!=EOF){
        if(n==0)
             break;
        map<string,int> id;
        vector<int> edge[maxn];
        memset(judge,0,sizeof(judge));
        memset(d,0,sizeof(d));
        cnt=1;
        cin>>temp;
        for(int i=0;i<n-1;i++){
            cin>>names;
            cin>>namef;
            if(!(id[namef]>=1&&id[namef]<=n)){
               id[namef]=cnt++;
            }
            if(!(id[names]>=1&&id[names]<=n)){
               id[names]=cnt++;
            }
            int u=id[namef];
            int v=id[names];
            edge[u].push_back(v);
        }
        int st;
        if(n==1){
          st=1;
        }
        else st=id[temp];
        dfs(st,edge);

        //cout<<ans1<<" "<<ans2<<endl;
        //cout<<d[1][0]<<" "<<d[1][1]<<endl;
        if(d[st][0] == d[st][1])  printf("%d No\n", d[st][0]);//判断谁在数更大
        else if(d[st][0] > d[st][1])  printf("%d %s\n",d[st][0], judge[st][0] ? "No" : "Yes");
        else  printf("%d %s\n", d[st][1], judge[st][1] ? "No" : "Yes");
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值