Codevs 1421:秋静叶&秋穣子——题解
来源:互联网 发布:mac 双系统丢失 编辑:程序博客网 时间:2024/06/06 20:37
勇者时常喜欢玩游戏,还喜欢用路由器的钱买游戏,还喜欢用路由器的电脑玩游戏。
有一天,他玩到了东方project的同人游(题)戏(目)
——————————————————————
题目描述 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]。
勇者玩了2^31-1次都没有成功,于是找到了路由器。
路由器表示,这题刚开始想的思路还是蛮对的,这题是一道dp+树+博弈论的题。
dp的含义也很简单想,数组dp[i][0]表示以i为根先手能获得的叶子数量,数组dp[i][1]表示以i为根后手能获得的叶子数量。
然后为了知道当前实际操作的人是谁,我们用dep记录当前的深度,dep%2==1就是静叶走,反之为穰子。
从下往上搜,一直推到dp[root(根节点)]就行啦!
……好的,到这里路由器就不会往下推了,看了题解。
尼玛怎么题解都是清一色的!
这里复制一下给大家看,能看懂的人就不用往下看了(都是巨佬TAT):
当dep为奇[静叶] (设dep[root]=1)
dp[i][0]=dp[k][1]+num[i]
dp[i][1]=f[k][0]
k是i的儿子,且代表dp[k][0]取最大时的k,当dp[k][0]相同时,取dp[k][1]最小。(穰子最优)
当dep为偶时[穰子]
dp[i][0]=dp[k][1]+num[i]
dp[i][1]=dp[k][0]
k是i的儿子,且代表dp[k][1]取最小时的k,当dp[k][1]相同时,取dp[k][0]最大。(静叶最优)
还有一个版本可能更好理解?也粘在下面了(至少我是看这个看懂的):
对于depth&1==1的情况:
dp[i][0]=Num[i]+dp[k][1];
dp[i][1]=dp[k][0];
k是i的儿子,k为max{dp[k][0]}取得最大时的k,dp[k][0]相同时,取dp[k][1]最小的;
即我是先手(全局的先手),我只能取奇数深度的根节点,然后我的对手便会按照他的准则来取。他的准则便是让自己尽量多,所以他会在保证dp[k][0](他是全局的后手,那么他便是以k为根子树的先手)最大的情况下,来让dp[k][1]最小,即让我尽量少。
对于depth&1==0的情况:
同样地:dp[i][0]=Num[i]+dp[k][1];
dp[i][1]=dp[k][0];
k是i的儿子,k为min{dp[k][1]}取得最小时的k,dp[k][1]相同时,取dp[k][0]最大的。
即我是后手(全局的后手),我只能取偶数深度的根节点,而当前节点深度正好为偶数。我对手的准则让我尽量少,所以他会在保证dp[k][1]尽量小的情况下(我是全局后手,又k为奇数节点,所以我为k的后手),让dp[k][0]尽量大,即让我的对手尽量多。
——————————————————
大佬们看懂了吗(至少我开始时没听懂)?
好的如果没懂的话,我们来复述一遍过程。
我现在在k节点。k的儿子统一叫i。
假设我是静叶(全局的先手),我知道对手的策略是:在自己尽可能取得多的前提下,让静叶取得最少。那么她一定会走我所在节点的儿子当中叶子最多的(如果有多个,则走会使得我最终得到叶子最少的节点)。那么对应的,就是先保证dp[i][0]是所有i中最大的,如果有多个最大,那么让dp[i][1]最小。走符合上述条件的点i即可。
假设我是穰子(全局的后手),我知道对手的策略是:在让穰子取得尽可能少的前提下,自己取的最多。那么她一定走会使得我最终得到叶子最少的节点(如果有多个,则走我所在节点的儿子当中叶子最多的)。那么对应的,就是先保证dp[i][1]最小,如果有多个最小,那么让dp[i][0]是所有i中最大的。走符合上述条件的点i即可。
那么我们就确定了我们接下来要走的节点i,有如下关系:
dp[k][0]=dp[i][1]+num[k];//继承下次自己的操作所获得的的叶子+自己所在的节点的全部叶子。
dp[k][1]=dp[i][0];//继承下次自己的操作所获得的叶子。
好的我们来处理几个小事情。
1.dp初始化什么呢?
答:你有两个选择:
1.你可以按照下面的代码一样,所有dp都是0,然后我们存节点数的t=0;//这个不太好想,推荐第二种
2.当我们到最后一个节点即走不下去的时候,我们很明显的发现,dp[i][0]=num[i],dp[i][1]=0;
2.如何找根节点呢?
答:很简单,我们可以找到没有父亲的那个节点,那个节点就是root。
3.我莫名其妙的就TLE怎么办?
答:多半是由于你使用了找父亲的方法找儿子所以会TLE,所以果断使用链式前向星。
4.有两个点死活没过去,和标准答案差了两位数怎么办
答:INF开大点,开到九位数。
那么来看代码吧。
以上,就是路由器两周的成果……(路由器想骂人怎么办?)
#include<cstdio>#include<cstring>#include<cmath>#include<string>#include<algorithm>using namespace std;int fa[100001]={0},num[100001];int dp[100001][2]={0};int n;int cnt=0,head[100001]; struct{ int to; int next; }edge[100001];void add(int u,int v){//u起点v终点 cnt++; edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt;}const int INF=999999999;//注意开大点!!!void dfs(int k,int dep){ int minn=INF,maxn=-1; int l=0; if(dep%2==1){ for(int i=head[k];i!=0;i=edge[i].next){ int t=edge[i].to; dfs(t,dep+1); if(dp[t][0]>maxn||(dp[t][0]==maxn&&dp[t][1]<minn)){ maxn=dp[t][0]; l=t; minn=dp[t][1]; } } dp[k][0]=dp[l][1]+num[k]; dp[k][1]=dp[l][0]; } else{ for(int i=head[k];i!=0;i=edge[i].next){ int t=edge[i].to; dfs(t,dep+1); if(dp[t][1]<minn||(dp[t][0]>maxn&&dp[t][1]==minn)){ minn=dp[t][1]; l=t; maxn=dp[t][0]; } } dp[k][0]=dp[l][1]+num[k]; dp[k][1]=dp[l][0]; } return;}int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&num[i]); } for(int i=1;i<=n-1;i++){ int u,v; scanf("%d%d",&u,&v); fa[v]=1; add(u,v); } int qi; for(int i=1;i<=n;i++){ if(fa[i]==0){ qi=i; break; } } dfs(qi,1); printf("%d %d",dp[qi][0],dp[qi][1]); return 0;}
- Codevs 1421:秋静叶&秋穣子——题解
- CodeVS 4888:零件分组——题解
- Codevs 1036:商务旅行——题解
- Codevs 1163:访问艺术馆——题解
- codevs 2597 团伙 题解
- Codevs 1076 题解
- Codevs 1201 题解
- CodeVS 第四次月赛 题解
- Codevs 多米诺+覆盖题解
- 【codevs 1191】题解
- 【codevs 1369】题解
- 【codevs 2981】题解
- 【codevs 1690】题解
- CodeVS 1215 迷宫 题解
- CodeVS 3344 迷宫 题解
- CodeVS 1710 生日蛋糕 题解
- CodeVS 1102 采药 题解
- CODEVS 1029 遍历问题 题解
- 数据处理阶段(一)
- 【QT】Item中添加控件
- Getting unknown property: app\models\CategorySearch::content
- Handle & Inner Classes 如何避免内存泄漏
- Oracle数据库的start with ... connect by
- Codevs 1421:秋静叶&秋穣子——题解
- 关于安装完Myeclipse之后的一系列配置操作
- Android7.0中文文档(API) -- ActionMenuView.OnMenuItemClickListener
- 进程遍历
- liunx下cpu占用率高如何定位代码问题
- dflist0614
- 虚拟机无法联网
- 解决android 对话框 宽度被限定在 65%的问题及背景透明问题
- redis学习日志二(windows操作系统redis的安装和启动)