ICPC Southeastern Europe Contest 2019 E. Game on a Tree(树形DP)

题目链接
题目大意:
给定一棵树,然后Alice和Bob进行博弈,首先Alice选择一个点染色,然后Bob可以将这个点的子节点或者他的祖先进行染色,已经染色的顶点不能再次到达,谁不能动了谁就输了。

解题思路:
想题的时候先把树变成图,将本节点和其祖先连接起来(无向边),所以就是图上的匹配问题(就是在图上找出边的子集,使这些边没有公共顶点)。最大匹配就是找出的边数最多,完美匹配就是所有顶点都属于其中一个边。
结论:如果最大匹配等于完美匹配就是Bob赢,否则就是Alice赢。
证明:
假设现在已经是最大匹配等于完美匹配,因为是匹配,每条边都有两个顶点(v11,v12),(v21,v22)…,所以每个顶点都属于一个匹配,所以不管Alice选择哪个顶点,Bob只要选与他匹配的顶点就可以了,然后Alice再选择一个与B选择相连的顶点,Bob再选择其匹配的点,以此类推,所以就是Bob赢。

假设现在最大匹配不等于完美匹配,就是有至少一个顶点没有被匹配,则Alice先选取一个未匹配的点,Bob就只能选择已经被匹配的点了,如果Bob也能选择一个未匹配,则这两个点会构成一个新的匹配,就与最大匹配定义矛盾。如果bob在后续第k次选择中选到了未匹配的点,则这个点可以与Alice的第k次选择的点进行匹配,则可以往前递推到最前面,发现会构成一条新的匹配,一样与最大匹配矛盾。

如果按思路这样建图则图太大的了,所以就在树上进行匹配。
定义dp[u]为以u为根的子树这些点中所有未匹配的点。
1.如果u是叶子节点,dp[u]=1
2.如果u不是叶子节点,则对所以儿子的未匹配数求和为D,如果D>0,则表示后代节点有未匹配,所以用这个点去匹配,dp[u]=D-1。如果没有未匹配点,则u点就为未匹配点,则dp[u]=1。

实验代码:

#include
using namespace std;
mt19937 rng_32(chrono::steady_clock::now().time_since_epoch().count());
typedef long long ll;
const int maxn=2e5+10;
struct E{
    int to,nxt;
    E(){};
    E(int t1,int n1)
    {
        to=t1;
        nxt=n1;
    }
}e[maxn];
int head[maxn],tot;
void adde(int u,int v)
{
    e[++tot]=E(v,head[u]);
    head[u]=tot;
}
int dp[maxn];
int dfs(int u,int fa)
{
    bool leaf=true;
    for (int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if (to==fa)
        continue;
        leaf=false;
        dp[u]+=dfs(to,u);
    }
    if (leaf)
    dp[u]=1;
    else
    {
        if (dp[u]>0)
        dp[u]--;
        else
        dp[u]=1;
    }
    return dp[u];
}
int main()
{
    int n,t1,t2;
    cin>>n;
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&t1,&t2);
        adde(t1,t2);
        adde(t2,t1);
    }
    if (dfs(1,1)==0)
        cout<<"Bob";
    else
        cout<<"Alice";
    return 0;
}

你可能感兴趣的:(计蒜客)