[51nod 1531]树上的博弈

题目描述

有一棵n个点的有根树,他有m个叶子结点(叶子结点是那些没有孩子的结点)。边由父亲指向孩子。数字1到m被分配到每一个叶子中。每一个叶子有一个数字,并且每一个数字恰好被分配到一个叶子中。
刚开始的时候根部有一个棋子。两个玩家轮流移动棋子,每一步都会将这个棋子向他的某一个孩子移动;如果玩家不能再移动棋子了,那么游戏结束。游戏的结果就是棋子所在叶子上面的数字。游戏的先手想要这个数字最大化,而后手想要这个数字最小化。
山巴布里想要给这些叶子分配数字使得最终结果最大,而马族塔想要给这些叶子分配数字使得最终结果最小。那么当山巴布里来分配数字的时候游戏结果会是多少?马族塔分配的时候又是多少呢?山马布里和马族塔并不参加游戏,而是另外两个非常聪明的人来参加游戏。
样例解释:在这个样例中,树有三个叶子:3,4和5。如果把数字3分配给3号结点,那么第一个选手就能够让棋子到达那儿,最终结果就是3。另一方面,很明显,无论怎么分配数字,第一个选手让棋子达到最小数字是2。

DP

考虑最大化怎么做。
假设我的答案是ans,设f[i]表示i子树内需要至少安置多少个>=ans才能使在这个节点往后操作一定能使答案为ans。
显然i是先手操作就对每颗子树的f取min,否则求和。
显然m-f[1]是最大答案。
最小化同理。

#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=200000+10;
int f[maxn],d[maxn],h[maxn],go[maxn],next[maxn];
int i,j,k,l,r,mid,n,m,ans,tot;
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void dfs(int x){
    int t=h[x];
    if (!t){
        m++;
        return;
    }
    while (t){
        d[go[t]]=d[x]+1;
        dfs(go[t]);
        t=next[t];
    }
}
void dg(int x){
    int t=h[x];
    if (!t){
        f[x]=1;
        return;
    }
    if (d[x]%2==0){
        f[x]=m+1;
        while (t){
            dg(go[t]);
            f[x]=min(f[x],f[go[t]]);
            t=next[t];
        }
    }
    else{
        f[x]=0;
        while (t){
            dg(go[t]);
            f[x]+=f[go[t]];
            t=next[t];
        }
    }
}
void dg2(int x){
    int t=h[x];
    if (!t){
        f[x]=1;
        return;
    }
    if (d[x]%2==1){
        f[x]=m+1;
        while (t){
            dg2(go[t]);
            f[x]=min(f[x],f[go[t]]);
            t=next[t];
        }
    }
    else{
        f[x]=0;
        while (t){
            dg2(go[t]);
            f[x]+=f[go[t]];
            t=next[t];
        }
    }
}
int main(){
    scanf("%d",&n);
    fo(i,1,n-1){
        scanf("%d%d",&j,&k);
        add(j,k);
    }
    dfs(1);
    dg(1);
    printf("%d ",m-f[1]+1);
    dg2(1);
    printf("%d\n",f[1]);
}

你可能感兴趣的:(树形动规)