博弈dp入门 POJ - 1678 HDU - 4597
本来博弈还没怎么搞懂,又和dp搞上了,哇,这真是冰火两重天,爽哉妙哉。
我自己的理解就是,博弈dp有点像对抗搜索的意思,但并不是对抗搜索,因为它是像博弈一样,大多数以当前的操作者来dp,光想是想不通的,上题练一练。
POJ - 1678 I Love this Game!
题目大意:有两个人正在玩游戏,在给出的一堆数中,玩家1先在[a,b]的范围中挑选出一个x1,然后玩家2再挑选有个y1满足a≤y1-x1≤b,然后玩家1再挑选一个x2满足a≤x2-y1≤b,以此轮流操作,直到某个玩家不能再进行操作,此时玩家1的分值为x1+x2+...,玩家2的分值为y1+y2+...,两个玩家都足够聪明,求最大的分差是多少?
其实不管就算是在博弈里,我觉得最难理解的也就是那句都足够聪明(我不够聪明怎么办),然后就在想如果足够聪明接下来会怎么做,然后推推推半天又不确定自己模拟的是不是就是都足够聪明下的情况,其实反过来想,既然都足够聪明,那么他们都已经意料到这场博弈的结果如何,所以说是当前状态博弈出最后状态,更不如说是最后状态反演出当前状态,已知结果时当前会做怎样的抉择(个人理解).
所以放在这题就是这么思考,首先因为0<a,而a≤x1≤b,x1<y1<x2<y2<..所以我们可以先把非正整数的数过滤掉,只留下正整数,然后再排序,这样就是在一个有序的的正整数序列中选择,那么我们就可以dp[i]就是当前选择了第i个数之后的最大分差,注意是当前,不是先手,也不是后手。那么当前(第i轮)选择完后,(第i+1轮)换下一个人就是下一个人是当前,那第i+1轮的人选择完后,他肯定保证自己是最大分差,所以回到第i轮,能取到的最大分差就是x[i]-max(第i+1的分差),有点绕,详情见代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=;
const int inf=0x3f3f3f3f;
int t,n,m,a,b,x[N],dp[N];//dp[i]为当前操作者取走第i个数的最大分差
int by(int p)
{
if(dp[p]!=-inf)//类似记忆化搜索
return dp[p];
int ans=-inf;
for(int i=p+;i<m&&x[i]-x[p]<=b;i++)
if(x[i]-x[p]>=a)//为什么取最大呢,因为是相等于当前取了p这个数,然后操作轮到下一个人,
ans=max(ans,by(i));//而下一个人足够聪明,那么他的分差应该是最大的
if(ans==-inf)//如果下一个人已经不能操作了,当前最大分差就是当前取的分值
return dp[p]=x[p];
return dp[p]=x[p]-ans;//当前取的分值减去下一操作者能取得的最大分差就是当前最大分差
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&a,&b);
m=;
for(int i=;i<=n;i++)
{
dp[i]=-inf;
scanf("%d",&x[i]);
if(x[i]>)//过滤掉非正整数
x[m++]=x[i];
}
sort(x+,x+m);
x[]=,dp[]=-inf;
printf("%d\n",-by());//因为我多加了个0作为先手,返回的是0-ans,所以答案要取负
}
return ;
}
说好的都聪明,我怎么就不聪明呢
HDU - 4597 Play Game
题目大意:Alice和Bob(这两博弈大佬)在玩一个游戏,有两堆牌,每次Alice和Bob只能从牌堆的两边取走一张牌,并得到相应的分数,问Alice先手最大能取得的分数是多少?
说是博弈dp,其实这题也属于区间dp,我们先用博弈dp解决,和第一题类似我们可以dp[i][j][k][l]表示第一堆还剩i~j,第二堆时还剩k~l时的当前操作者的最大分差,设分差为dis,alice得分a,bob得分b,所有牌取完的总分sum,有x-y=dis,x+y=sum,那么x=(sum+dis)/2,不过思路处理上和上一题有点出入,上一题是挑选了之后去由下一个人的最大分差来得到目前最大分差,这一题是当前有4种挑选结果,然后取最大的分差,详情见代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=,inf=0x3f3f3f3f;
int t,n,a[N],b[N],dp[N][N][N][N];//dp[i][j][k][l]第一堆牌剩i~j,第二堆牌剩k~l时当前操作者最大分差
int dfs(int la,int ra,int lb,int rb)
{
if(la>ra&&lb>rb)
return ;
if(dp[la][ra][lb][rb]!=inf)
return dp[la][ra][lb][rb];
int ans=-inf,a1,a2,b1,b2;
if(la<=ra)
{
a1=a[la]-dfs(la+,ra,lb,rb);//挑选a[la]后在下一个人的最大分差下能得到的最大分差
a2=a[ra]-dfs(la,ra-,lb,rb);//挑选a[ra]后在下一个人的最大分差下能得到的最大分差
ans=max(ans,max(a1,a2));
}
if(lb<=rb)
{
b1=b[lb]-dfs(la,ra,lb+,rb);//挑选b[lb]后在下一个人的最大分差下能得到的最大分差
b2=b[rb]-dfs(la,ra,lb,rb-);//挑选b[rb]后在下一个人的最大分差下能得到的最大分差
ans=max(ans,max(b1,b2));
}
return dp[la][ra][lb][rb]=ans;//四种情况取最大即当前最大分差
}
int main()
{
scanf("%d",&t);
while(t--)
{
int sum=;
scanf("%d",&n);
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
for(int i=;i<=n;i++)
{
scanf("%d",&b[i]);
sum+=b[i];
}
memset(dp,inf,sizeof(dp));
printf("%d\n",(sum+dfs(,n,,n))/);
}
return ;
}
博弈博弈,博一博,变容易
还有区间dp,就是dp[i][j][k][l]表示第一堆还剩i~j,第二堆时还剩k~l时,在剩余分的当前操作者能取到的最大分值。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=;
int t,n,a[N],b[N],dp[N][N][N][N];//dp[i][j][k][l]第一堆牌剩i~j,第二堆牌剩k~l时当前操作者最大分值
int dfs(int la,int ra,int lb,int rb,int sum)
{
if(la>ra&&lb>rb)
return ;
if(dp[la][ra][lb][rb]!=)
return dp[la][ra][lb][rb];
int ans=;
if(la<=ra)
{
int a1=sum-dfs(la+,ra,lb,rb,sum-a[la]);//剩余的分减去下一个操作者能得到的最大分
int a2=sum-dfs(la,ra-,lb,rb,sum-a[ra]);
ans=max(ans,max(a1,a2));
}
if(lb<=rb)
{
int b1=sum-dfs(la,ra,lb+,rb,sum-b[lb]);
int b2=sum-dfs(la,ra,lb,rb-,sum-b[rb]);
ans=max(ans,max(b1,b2));
}
return dp[la][ra][lb][rb]=ans;
}
int main()
{
scanf("%d",&t);
while(t--)
{
int sum=;
scanf("%d",&n);
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
for(int i=;i<=n;i++)
{
scanf("%d",&b[i]);
sum+=b[i];
}
memset(dp,,sizeof(dp));
printf("%d\n",dfs(,n,,n,sum));
}
return ;
}
区间区间,区区间间
博弈dp入门 POJ - 1678 HDU - 4597的更多相关文章
- dp入门--poj 1163数塔
...
- 博弈dp 以I Love this Game! POJ - 1678 为例
写在前面的话 知识基础:一些基础的博弈论的方法,动态规划的一些知识 前言:博弈论就是一些关于策略或者游戏之间的最优解,动态规划就是对于一些状态之间转移的一些递推式(or 递归),dp分为很多很多种,比 ...
- 【Mark】博弈类题目小结(HDU,POJ,ZOJ)
转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents by---cxlove 首先当然要献上一些非常好的学习资料: 基础博弈的小 ...
- poj 3254 状压dp入门题
1.poj 3254 Corn Fields 状态压缩dp入门题 2.总结:二进制实在巧妙,以前从来没想过可以这样用. 题意:n行m列,1表示肥沃,0表示贫瘠,把牛放在肥沃处,要求所有牛不能相 ...
- HDU 2084 数塔(简单DP入门)
数塔 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...
- POJ 2104&HDU 2665 Kth number(主席树入门+离散化)
K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 50247 Accepted: 17101 Ca ...
- HDU 5623 KK's Number (博弈DP)
KK's Number 题目链接: http://acm.hust.edu.cn/vjudge/contest/121332#problem/K Description Our lovely KK h ...
- POJ 2342 树形DP入门题
有一个大学的庆典晚会,想邀请一些在大学任职的人来參加,每一个人有自己的搞笑值,可是如今遇到一个问题就是假设两个人之间有直接的上下级关系,那么他们中仅仅能有一个来參加,求请来一部分人之后,搞笑值的最大是 ...
- HDU 2089 不要62【数位DP入门题】
不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
随机推荐
- Ruby Rails学习中:注册表单,注册失败,注册成功
接上篇 一. 注册表单 用户资料页面已经可以访问了, 但内容还不完整.下面我们要为网站创建一个注册表单. 1.使用 form_for 注册页面的核心是一个表单, 用于提交注册相关的信息(名字.电子邮件 ...
- JS 装饰器,一篇就够
更多文章,请在Github blog查看 在 ES6 中增加了对类对象的相关定义和操作(比如 class 和 extends ),这就使得我们在多个不同类之间共享或者扩展一些方法或者行为的时候,变得并 ...
- Docker——网络和存储(数据卷)
iptables -t nat -vnL |grep docker 查看docker桥接网卡:brctl show 本地端口随机映射到docker容器的80端口上: docker run -d -P ...
- Java 代码运行顺序
1.静态代码块,只执行一次,从上到下,先父类后子类 2.父类构造代码块,从上到下,然后父类构造方法,执行次数与实例化次数相关 3.子类构造代码块,从上到下,然后子类构造方法,执行次数与实例化次数相关
- Left4Dead2 LAN Online
Left4Dead2 LAN Online Franklin vs Wolverine 求生之路 局域网联机说明 ============================ 局域网联机方法: 1.先找到 ...
- js获取图片信息
网络图片: fetch(item.path).then(function(res){ // 计算图片大小 return res.blob() }).then(function(data){ conso ...
- 前后端分离-模拟数据之RAP2快速入门
是啥? RAP是一个可视化接口管理工具 通过分析接口结构,动态生成模拟数据,校验真实接口正确性, 围绕接口定义,通过一系列自动化工具提升我们的协作效率.我们的口号:提高效率,回家吃晚饭! 可视化编辑, ...
- 终端复用工具-tmux
目录 终端复用工具--Tmux 一.为什么要用Tmux? 二.tmux是什么? 三.Tmux基本概念 四.Tmux使用规则 1.安装Tmux 2.基本使用 3.自定义配置文件 五.补充 1.tmux ...
- 十二,k8s集群访问控制之RBAC授权
目录 角色访问控制RBAC (Role-Based Access Control) 常用的授权插件: RBAC控制: role 和 clusterrole rolebinding 和 clusterr ...
- RHEL6使用系统自带多路径软件配置多路径
1.多路径的主要功能 多路径一般配合存储设备实现如下功能: 故障的切换和恢复 IO流量的负载均衡 磁盘的虚拟化 2.查看系统自带的多路径软件是否安装 [root@cluster01 ~]# ...