【博弈论】The Showdown

有一个无向图,边分为红边和蓝边,两种边分别构成一棵树。两个人在两棵树上各一个点。

两人轮流移动,操作只有两个:走或不走。你只能走到与所在点相连的点上。

先手在红树上移动,后手在蓝树上移动。如果任意时刻两个人位于同一个点即可输出

红树需要最大化玩家的操作次数,蓝树需要最小化玩家的操作次数。求最终操作次数的值


建树,先建蓝树

对于一条红边\(xy\),若在蓝树上x到y路径长\(>2\),且先手走到该边端点时两人仍未相遇,则只能输出\(-1\)

把长度大于\(2\)的边删掉(称为“长边”)

很明显,走短边不可能走出蓝子树

因此先手走到一个能走到的深度最大的点之后等后手即可

#include<bits/stdc++.h>
#define MAXN 200005
using namespace std;

int n,cnt,last[MAXN];
int father[MAXN],deep[MAXN],dis[MAXN];
int q[MAXN],nnn[MAXN],mx[MAXN];
int tim,a[MAXN][2],xx,yy;
bool vis[MAXN],win[MAXN];

struct edge
{
    int to,next,h;
}t[MAXN*4];

void add(int u,int v,int h)
{
    t[++cnt].to=v;t[cnt].h=h;
    t[cnt].next=last[u];last[u]=cnt;
    t[++cnt].to=u;t[cnt].h=h;
    t[cnt].next=last[v];last[v]=cnt;
}

void dfs(int x)
{
    deep[x]=deep[father[x]]+1;nnn[x]=++tim;
    for (int i=last[x];i;i=t[i].next)
        if (t[i].to!=father[x]) father[t[i].to]=x,dfs(t[i].to);
    mx[x]=++tim;
}

bool check(int x,int y)
{
    if (nnn[x]>nnn[y]) swap(x,y);
    if (nnn[x]<nnn[y]&&mx[x]>nnn[y]) return deep[y]-deep[x]>2 ? true : false;
    if (father[x]==father[y]) return false;
    return true;
}

void bfs()
{
    int h=1,tt=1;q[1]=xx;vis[xx]=1;
    while (h<=tt)
    {
        int x=q[h++];
        for (int i=last[x];i;i=t[i].next)
            if (!t[i].h&&!vis[t[i].to])
            {
                dis[t[i].to]=dis[x]+1;
                if (dis[t[i].to]<deep[t[i].to]) vis[t[i].to]=1,q[++tt]=t[i].to;
            }
    }
}

int max(int a,int b){return a<b?a:b;}//

template<class T>inline void read(T &res)
{
    static char ch;T flag=1;
    while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1;res=ch-48;
    while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48;res*=flag;
}

int main()
{
    int ans=0;
    read(n);read(xx);read(yy);

    for (int i=1;i<n;i++) {read(a[i][0]);read(a[i][1]);}
    
    for (int i=1;i<n;i++)
    {
        int x,y;
        read(x);read(y);
        add(x,y,1);
    }

    deep[0]=-1;
    dfs( yy );

    for (int i=1;i<n;i++)
    {
        if (check(a[i][0],a[i][1]))
            win[a[i][0]]=win[a[i][1]]=1;
        else add(a[i][0],a[i][1],0);
    }

    bfs();

    for (int i=1;i<=n;i++)
        if (win[i]&&vis[i])
            {putchar('-');putchar('1');return 0;}

    for (int i=1;i<=n;i++)
        if (vis[i])
            ans=max(ans,deep[i]<<1);

    printf("%d\n",ans);
    return 0;
}

这题贪心比较好想,博弈策略一定要认真思考(我也是借鉴了\(dalao\)的思路才做出来的)

代码看一会儿就好了,代码里有一个很明显的错误防无脑抄

思维难度比代码难度大

猜你喜欢

转载自www.cnblogs.com/tqr06/p/10458892.html