题目大意:
在幻想乡,秋姐妹决定比比谁能够收集到最多的红叶。静叶将红叶分成了N堆(编号1..N),并且规定了它们的选取顺序,刚好形成一颗有向树。在游戏过程中,两人从根节点开始,轮流取走红叶,当一个人取走节点i的红叶后,另一个人只能从节点i的儿子节点中选取一个。当取到某个叶子时游戏结束,然后两人会比较自己得到的红叶数量。已知两人采用的策略不一样,静叶考虑在让穰子取得尽可能少的前提下,自己取的最多;而穰子想得是在自己尽可能取得多的前提下,让静叶取得最少。在两人都采取最优策略的情况下,请你计算出游戏结束时两人的红叶数量。
游戏总是静叶先取,保证只存在一组解。
数据范围
对于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]。
题解:
树上DP+博弈:
设f[i][0]表示以i为根的子树先手最优值,f[i][1]相应表示后手最优值,注意这里是先手跟后手,不是固定的静叶跟穰子。
当静叶是先手,穰子是后手,儿子为k:
你要使得f[k,0]最小的时候f[k,1]最大
f[i][0]=f[k][1]+num[i]
当穰子是先手,静叶是后手,儿子为k:
你要使得f[k,1]最大的时候f[k,0]最小
f[i][0]=f[k][1]+num[i]
这样的dp其实很容易实现,
虽然直接dfs可能会炸,但手工栈或从叶子节点逆着广搜推子节点就可以了。
codevs dfs竟然过了。。
代码:
var
next,list,a,x,y:array [0..100001] of longint;
f:array [0..100001,0..1] of longint;
b:array [0..100001] of boolean;
i,j,k,n,m:longint;
function max(aa,bb:longint):longint;
begin
if aa>bb then exit(aa);
exit(bb);
end;
procedure dfs(dep,rp:longint);
var
i,j,k,op,cp:longint;
begin
cp:=maxlongint;
op:=0;
k:=0;
if dep=1 then
begin
j:=list[rp];
while j>0 do
begin
dfs(0,y[j]);
if (f[y[j],0]>op) or ((f[y[j],0]=op) and (f[y[j],1]<cp))
then begin
op:=f[y[j],0];
cp:=f[y[j],1];
k:=y[j];
end;
j:=next[j];
end;
end
else begin
j:=list[rp];
while j>0 do
begin
dfs(1,y[j]);
if (f[y[j],1]<cp) or ((f[y[j],1]=cp) and (f[y[j],0]>op))
then begin
op:=f[y[j],0];
cp:=f[y[j],1];
k:=y[j];
end;
j:=next[j];
end;
end;
f[rp,0]:=f[k,1]+a[rp];
f[rp,1]:=f[k,0];
end;
begin
readln(n);
for i:=1 to n do read(a[i]);
for i:=1 to n-1 do
begin
readln(x[i],y[i]);
next[i]:=list[x[i]];
list[x[i]]:=i;
b[y[i]]:=true;
end;
for i:=1 to n do
if not(b[i]) then k:=i;
dfs(1,k);
writeln(f[k,0],' ',f[k,1]);
end.