NKOI 2505 秋静叶&秋穣子

39 篇文章 0 订阅
1 篇文章 0 订阅

1421 秋静叶&秋穣子

时间限制: 1 s
空间限制: 128000 KB
题目等级 : 大师 Master
题目描述 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,搞懂了其实非常简单的一道题

用f[u][0]表示以u为根的子树先手的最优值,f[u][1]表示以u为根的子树后手的最优值

在每一次dp搜索的时候我们传递一个参数dep表示当前讨论到的层数(dep[root]==1),这样如果dep&1==1就说明轮到静叶拿

对于depth&1==1的情况:

        F[i][0]=Num[i]+F[k][1];

        F[i][1]=F[k][0];

k是i的儿子,kF[k][0]取得最大时的k,F[k][0]相同时,取F[k][1]最小的

即我是先手(全局的先手),我只能取奇数深度的根节点,然后我的对手便会按照他的准则来取。他的准则便是让自己尽量多,所以他会在保证F[k][0](他是全局的后手,那么他便是以k为根子树的先手)最大的情况下,来让F[k][1]最小,即让我尽量少。


对于depth&1==0的情况:

同样地:F[i][0]=Num[i]+F[k][1];

                F[i][1]=F[k][0];

k是i的儿子,k为F[k][1]取得最小时的k,F[k][1]相同时,取F[k][0]最大的

即我是后手(全局的后手),我只能取偶数深度的根节点,而当前节点深度正好为偶数。我对手的准则让我尽量少,所以他会在保证F[k][1]尽量小的情况下(我是全局后手,又k为奇数节点,所以我为k的后手),让F[k][0]尽量大,即让我的对手尽量多。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
const int maxn=1e5+5,inf=0x3f3f3f3f;
inline void _read(int &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
int n,num[maxn],last[maxn],f[maxn][2];
int ind[maxn],outd[maxn],root;
struct node{
	int a,b,Next;
	node(int a,int b,int Next):a(a),b(b),Next(Next){}
};
vector<node>s;
void insert(int a,int b){
	s.push_back(node(a,b,last[a]));
	last[a]=s.size()-1;
}
void dp(int u,int dep){
	int i,j,v,maxx=-inf,minn=inf,id;
	if(!outd[u]){
		f[u][0]=num[u];
		return ;
	}
	if(dep&1){
		for(i=last[u];i>=0;i=s[i].Next){
			int v=s[i].b;
			dp(v,dep+1);
			if(f[v][0]>maxx||(f[v][0]==maxx&&f[v][1]<minn))
				maxx=f[v][0],minn=f[v][1],id=v;
		}
		f[u][0]=f[id][1]+num[u];
		f[u][1]=f[id][0];
	}
	else {
		for(i=last[u];i>=0;i=s[i].Next){
			int v=s[i].b;
			dp(v,dep+1);
			if(f[v][1]<minn||(f[v][1]==minn&&f[v][0]>maxx))
				maxx=f[v][0],minn=f[v][1],id=v;
		}
		f[u][0]=f[id][1]+num[u];
		f[u][1]=f[id][0];
	}
}
int main_main(){
	memset(last,-1,sizeof(last));
	_read(n);
	int i,j,x,y;
	for(i=1;i<=n;i++)_read(num[i]);
	for(i=1;i<n;i++){
		_read(x);_read(y);
		insert(x,y);
		outd[x]++,ind[y]++;
	}
	for(i=1;i<=n;i++)
	    if(!ind[i]){
	    	root=i;
	    	break;
		}
	dp(root,1);
	cout<<f[root][0]<<" "<<f[root][1];
}
const int main_stack=16;  
char my_stack[128<<20];  
int main() {  
  __asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");  
  __asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp");  
  main_main();  
  __asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");  
  return 0;  
}
/*
6 
4 16 16 5 3 1 
1 2 
2 4 
1 3 
3 5 
3 6 
*/




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值