【Codevs1421】【树上DP】【博弈论】秋静叶&秋穰子

东方幻想乡系列模拟赛th04的题.
也不是很难嘛…

题目描述 Description

在幻想乡,秋姐妹是掌管秋天的神明,作为红叶之神的姐姐静叶和作为丰收之神的妹妹穰子。如果把红叶和果实联系在一起,自然会想到烤红薯。烤红薯需要很多的叶子,才能把红薯烤得很香,所以秋姐妹决定比比谁能够收集到最多的红叶。静叶将红叶分成了N堆(编号1..N),并且规定了它们的选取顺序,刚好形成一颗有向树。在游戏过程中,两人从根节点开始,轮流取走红叶,当一个人取走节点i的红叶后,另一个人只能从节点i的儿子节点中选取一个。当取到某个叶子时游戏结束,然后两人会比较自己得到的红叶数量。已知两人采用的策略不一样,静叶考虑在让穰子取得尽可能少的前提下,自己取的最多;而穰子想得是在自己尽可能取得多的前提下,让静叶取得最少。在两人都采取最优策略的情况下,请你计算出游戏结束时两人的红叶数量。
  游戏总是静叶先取,保证只存在一组解。
输入描述 Input Description

第1行:1个正整数N,表示红叶堆数
  第2行:N个整数,第i个数表示第i堆红叶的数量num[i]
  第3..N+1行:2个正整数u,v,表示节点u为节点v的父亲
输出描述 Output Description

第1行:2个整数,分别表示静叶取到的叶子数和穰子取到的叶子数
样例输入 Sample Input

6
4 16 16 5 3 1
1 2
2 4
1 3
3 5
3 6
样例输出 Sample Output

7 16
数据范围及提示 Data Size & Hint

数据范围
  对于30%的数据:1 ≤ N ≤ 100,1 ≤ num[i] ≤ 100
  对于60%的数据:1 ≤ N ≤ 10,000,1 ≤ num[i] ≤ 10,000
  对于100%的数据:1 ≤ N ≤ 100,000,1 ≤ num[i] ≤ 10,000
 提示
  样例解释:
  首先静叶一定能取得节点1的4片红叶,留给穰子的是节点2和3,均为16片红叶。
  若选取节点2则静叶下一次可以最多得到5片红叶,而选择3静叶最多也只能得到3片红叶,
  所以此时穰子会选择节点3,故静叶最后得到的红叶数为7,穰子为16。

  注意:
  保证两人得到的红叶数在[0, 2^31-1]。

注释里都写清楚了自己看看就好
自下向顶DP
黄学长blog里说要写手工栈
可能是比赛时候windows评测系统栈比较小
反正我没写手工栈Codevs也A了

//AC code by CreationAugust
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXINT 0x7fffffff
using namespace std;
int f[100010][2];//某棵子树秋静叶或者秋穰子的最优值.DP过程自下向顶更新
//0为秋静叶1为秋穰子 
int num[100010];
int deep[100010];//记录一下树上每个节点的深度
//若为奇数就轮到秋静叶选否则秋穰子选 
int u,v;
int fa[100010];
int root;
int n;
struct edge
{
    edge *next;
    int to;
}line[100010],*prev[100010];
int ans;
void dfs(int x)
{
    int maxn=0,minn=MAXINT,t=0;
    if (deep[x]%2==1)//此处为秋静叶选取 
    {
        for (edge *i=prev[x];i;i=i->next)
        {
            int a=i->to;
            deep[a]=deep[x]+1;
            dfs(a);
            if ((f[a][0]>maxn)||(f[a][0]==maxn&&f[a][1]<minn))//这种情况下为秋穰子最多 
            {
                maxn=f[a][0];
                minn=f[a][1];
                t=a;
            }//找到最优的节点t
        }
        f[x][0]=f[t][1]+num[x];
        f[x][1]=f[t][0];
    }
    else//此处为秋静叶选取 
    {
        for (edge *i=prev[x];i;i=i->next)
        {
            int a=i->to;
            deep[a]=deep[x]+1;
            dfs(a);
            if ((f[a][1]<minn)||(f[a][1]==minn&&f[a][0]>maxn))
            {
                maxn=f[a][0];
                minn=f[a][1];
                t=a;
            }
        }
        f[x][0]=f[t][1]+num[x];
        f[x][1]=f[t][0];
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d",&num[i]);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        line[i].to=v;
        line[i].next=prev[u];
        prev[u]=&line[i];
        fa[v]=u;
    }
    for (int i=1;i<=n;i++)
        if (!fa[i])
        {
            root=i;
            break;
        }
    deep[root]=1;
    dfs(root);
    printf("%d %d",f[root][0],f[root][1]);
}
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/CreationAugust/article/details/44004451
文章标签: dp Codevs
上一篇树链剖分学习笔记
下一篇【SDOI2011】【BZOJ2243】【树链剖分】染色
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭