1. 程式人生 > >【博弈論】The Showdown

【博弈論】The Showdown

兩個人 最大化 deep main 人在 有一個 win 思維 true

有一個無向圖,邊分為紅邊和藍邊,兩種邊分別構成一棵樹。兩個人在兩棵樹上各一個點。

兩人輪流移動,操作只有兩個:走或不走。你只能走到與所在點相連的點上。

先手在紅樹上移動,後手在藍樹上移動。如果任意時刻兩個人位於同一個點即可輸出

紅樹需要最大化玩家的操作次數,藍樹需要最小化玩家的操作次數。求最終操作次數的值


建樹,先建藍樹

對於一條紅邊\(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\)的思路才做出來的)

代碼看一會兒就好了,代碼裏有一個很明顯的錯誤防無腦抄

思維難度比代碼難度大

【博弈論】The Showdown