1421 秋静叶&秋穣子
在幻想乡,秋姐妹是掌管秋天的神明,作为红叶之神的姐姐静叶和作为丰收之神的妹妹穰子。如果把红叶和果实联系在一起,自然会想到烤红薯。烤红薯需要很多的叶子,才能把红薯烤得很香,所以秋姐妹决定比比谁能够收集到最多的红叶。静叶将红叶分成了N堆(编号1..N),并且规定了它们的选取顺序,刚好形成一颗有向树。在游戏过程中,两人从根节点开始,轮流取走红叶,当一个人取走节点i的红叶后,另一个人只能从节点i的儿子节点中选取一个。当取到某个叶子时游戏结束,然后两人会比较自己得到的红叶数量。已知两人采用的策略不一样,静叶考虑在让穰子取得尽可能少的前提下,自己取的最多;而穰子想得是在自己尽可能取得多的前提下,让静叶取得最少。在两人都采取最优策略的情况下,请你计算出游戏结束时两人的红叶数量。
游戏总是静叶先取,保证只存在一组解。
第1行:1个正整数N,表示红叶堆数
第2行:N个整数,第i个数表示第i堆红叶的数量num[i]
第3..N+1行:2个正整数u,v,表示节点u为节点v的父亲
第1行:2个整数,分别表示静叶取到的叶子数和穰子取到的叶子数
6
4 16 16 5 3 1
1 2
2 4
1 3
3 5
3 6
7 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
提示
样例解释:
首先静叶一定能取得节点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的儿子,k为F[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
*/