第二题:游戏(提高组第二试2011年10月22日)(2011年NOIP冲刺模拟试题)

游 戏

【问题描述】

话说前年Alice和Bob相聚在一起玩游戏后,彼此都很忙,因此很少见面。今年由于为了NOIP2011又再次相聚,他们俩还是想比比谁能够收集到最多的石子数量。Alice将石子分成了N堆(编号1..N),并且规定了它们的选取顺序,刚好形成一颗有向树。在游戏过程中,两人从根节点开始,轮流取走石子,当一个人取走节点i的石子后,另一个人只能从节点i的儿子节点中选取一个。当取到某个叶子时游戏结束,然后两人会比较自己得到的石子数量。已知两人采用的策略不一样,Alice考虑在让Bob取得尽可能少的前提下,自己取的最多;而Bob想得是在自己尽可能取得多的前提下,让Alice取得最少。在两人都采取最优策略的情况下,请你计算出游戏结束时两人的石子数量。

    游戏总是Alice先取,保证只存在一组解。

 

【输入】

第1行只有一个正整数N,表示石子堆数;

第2行包含N个整数,第i个数表示第i堆石子的数量num[i];

第3..N+1行,每行两个正整数u和v,表示节点u为节点v的父亲;

 

【输出】

输出文件仅一行为两个整数,分别表示Alice取到的石子数和Bob取到的石子数。

 

【样例输入输出】

game.in

game.out

6

4 16 16 5 3 1

1 2

2 4

1 3

3 5

3 6

7 16

 

 

【样例解释】

首先Alice一定能取得节点1的4个石子,留给Bob的是节点2和3,均为16个石子。若选取节点2则Alice下一次可以最多得到5个石子,而选择3,则Alice最多也只能得到3个石子,所以此时Bob会选择节点3,故Alice最后得到的石子数为7,Bob为16。

 

【数据范围】

对于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

【注意】

保证两人得到的石子数在[0,2^31-1]。

/******************************************************************************************************
 ** Copyright (C) 2011.07.01-2013.07.01
 ** Author: famousDT <13730828587@163.com>
 ** Edit date: 2011-10-25
******************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>//abs,atof(string to float),atoi,atol,atoll
#include <math.h>//atan,acos,asin,atan2(a,b)(a/b atan),ceil,floor,cos,exp(x)(e^x),fabs,log(for E),log10
#include <vector>
#include <queue>
#include <map>
#include <time.h>
#include <set>
#include <list>
#include <stack> 
#include <string>
#include <iostream>
#include <assert.h>
#include <string.h>//memcpy(to,from,count
#include <ctype.h>//character process:isalpha,isdigit,islower,tolower,isblank,iscntrl,isprll
#include <algorithm>
using namespace std;

typedef long long ll;

#define MY_PI acos(-1)
#define MY_MAX(a, b) ((a) > (b) ? (a) : (b))
#define MY_MIN(a, b) ((a) < (b) ? (a) : (b))
#define MY_MALLOC(n, type) ((type *)malloc((n) * sizeof(type)))
#define MY_ABS(a) (((a) >= 0) ? (a) : (-(a)))
#define MY_INT_MAX 0x7fffffff
/*
6
4 16 16 5 3 1
1 2
2 4
1 3
3 5
3 6
*/
vector<int> son[100005];
int flag[100005] = {0};//判断是否是根 
int d[100005];  //每个节点的权值 
int dsize[100005];
int win[100005];//先手还是后手 
int ans[100005];//层次化保存树的结构 
int answer[100005][2];//保存最终结果
int path[100005];//保存路径 
int n;
int main()
{
    FILE *in, *out;
    in = freopen("game.in", "rt", stdin);
    out = freopen("game.out", "wt", stdout);
    scanf("%d", &n);
    int i, j, x, y, root;
    for (i = 1; i <= n; ++i)
        scanf("%d", &d[i]);
    for (i = 0; i < n - 1; ++i) {
        scanf("%d%d", &x, &y);
        flag[y] = 1;
        son[x].push_back(y);
    }
    for (i = 1; i <= n; ++i) if (!flag[i]) break;
    root = i;
    win[root] = 1;
    
    int tmp;//层次化遍历树,标记先手后手并记录路径 
    ans[0] = 0;
    ans[++ans[0]] = root; 
    path[root] = -1;
    for (i = 1; i <= ans[0]; ++i) {
        tmp = ans[i];
        int len = son[tmp].size();
        for (j = 0; j < len; ++j) {
            if (win[son[tmp][j]] == 0) {
                win[son[tmp][j]] = 3 - win[tmp];
                path[son[tmp][j]] = tmp;
                ans[++ans[0]] = son[tmp][j];
            }
        }
    }
    
    memset(ans, 0, sizeof(ans));//清零重新计数 
    for (i = 1; i <= n; ++i) {//自底向上搜 
        answer[i][0] = 0x7fffffff;
        answer[i][1] = 0;
        dsize[i] = son[i].size();
        if (!dsize[i]) {//只记下叶节点,方便自底向上搜 
            ans[++ans[0]] = i;
            answer[i][0] = 0;
        }
    }
    for (i = 1; i <= ans[0]; ++i) {        
        y = ans[i];
        x = path[y];
        answer[y][0] += d[y];//0代表自己,1代表别人 
        if (x == -1) break;
        if (win[y] == 1) {
            if (answer[y][1] < answer[x][0] || (answer[y][1] == answer[x][0] && answer[y][0] > answer[x][1]))
                answer[x][0] = answer[y][1], answer[x][1] = answer[y][0];
        } else {
            if (answer[y][0] > answer[x][1] || (answer[y][0] == answer[x][1] && answer[y][1] < answer[x][0]))
                answer[x][0] = answer[y][1], answer[x][1] = answer[y][0];
        }
        --dsize[x];
        if (!dsize[x]) ans[++ans[0]] = x;//内部节点变成了新的叶子节点 
    }
    printf("%d %d\n", answer[root][0], answer[root][1]);     
    system("pause");
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值