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

来源:互联网 发布:centos7 yum安装wget 编辑:程序博客网 时间:2024/06/05 16:53
题目链接

题意:
狼人杀投票环节,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