解题报告:武大第十五届校赛网络赛 H.Werewolf 树型DP


题意:
狼人杀投票环节,n个人进行投票,狼一定不会投狼,人可以任意投(除自己),现给出每个人的投票情况,问最多可能会有多少头狼。


思路:
比赛的时候没有多想,一直用树型DP去写,后来发现会有环的情况出现,没有想到解决办法。。。
赛后被题解点拨一下,发现两轮DFS可以解决环的情况,第一轮判断当前点所在的图有没有环,如果有,找出环上相邻的两个点,然后第二轮分别枚举环上的相邻的两个点作为起点和终点,终点不能选做狼人,然后就是基础的树型DP的模型。。。

代码:
#include<bits/stdc++.h>

#define fi first
#define se second
#define pii pair<int,int>
using namespace std;

vector<int>G[500005];
int n;
int a,b;
int dp[500005][2];
bool vis[500005];
bool VIS[500005];
vector< pair<int,int> >V;

void dfs(int x,int fa=0){
    VIS[x]=true;
    for(int i=0;i<G[x].size();i++){
        int &j = G[x][i];
        if(j==fa)continue;
        if(VIS[j]){
            a = x ;
            b = j ;
        }else {
            dfs(j,x);
        }
    }
}

void dfs2(int x){
    vis[x]=true;
    dp[x][1]=(x!=b);
    dp[x][0]=0;
    for(int i=0;i<G[x].size();i++){
        int &j = G[x][i];
        if(x==a&&j==b&&i!=G[x].size()-1)continue;
        if(!vis[j]){
            dfs2(j);
            dp[x][1] += dp[j][0];
            dp[x][0] += max(dp[j][1],dp[j][0]);
        }
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1,x;i<=n;i++){
        scanf("%d",&x);
        V.emplace_back( pii(min(i,x),max(i,x)) );
    }sort(V.begin(),V.end());
    pii t ;
    for(int i=0;i<V.size();i++){
        if(!i||V[i]!=V[i-1]){
            t = V[i];
            G[t.fi].emplace_back(t.se);
            G[t.se].emplace_back(t.fi);
        }
    }

    int ans = 0;
    for(int i=1;i<=n;i++){
        if(VIS[i])continue;
        a = i;b = 0;
        dfs(i);
        memset(vis,0,sizeof(vis));
        dfs2(a);
        int t = max(dp[a][1],dp[a][0]);;
        if(b){
            swap(a,b);
            memset(vis,0,sizeof(vis));
            dfs2(a);
            t = max(t,max(dp[a][1],dp[a][0]));
        }
        ans  += t;
    }printf("%d\n",ans);
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值