首页 > 其他分享 >博弈dp

博弈dp

时间:2022-09-19 15:35:33浏览次数:47  
标签:博弈 int Alice && Bob 平局 dp

博弈dp

D. Letter Picking

题意:现有偶数长度的字符串 s 。Alice和Bob进行以下游戏:

  • 每一回合,每人选择取走字符串首个字符,或取走末尾字符。
  • 每人按获得顺序倒序排列他们取得的字符,得到各自的字符串。
  • 最终得到的字符串字典序较小者胜利。相等则平局。

现两人足够聪明,求最终结果。

分析:设 \(dp_{l,r}\) 表示对于 \(l..r\) 的子字符串,其胜利者 1 表示Alice获胜, −1 表示Bob获胜, 0 表示平局。

如果 \(r−l+1=2\) ,若两个字符相同则平局,否则Alice获胜。

对于其他的情况:

  • 如果,Alice选了 sl ,并且 dpl+1,r−1 和 dpl+2,r 均为 1 ,这表明在字符串的前几个字符中,Alice的字典序较小,所以不用比较后面字符,他一定能获胜。
  • 同理,如果Alice选了 sr ,并且 dpl+1,r−1 和 dpl,r−2 均为 1 ,Alice获胜。
  • 如果无论 Alice选择 sl 还是 sr ,对应的两个 dp 中都有 −1 ,则Bob获胜。

对应的代码:

if(min(dp[l+2][r], dp[l+1][r-1]) == 1) {dp[l][r] = 1; continue;}
if(min(dp[l][r-2], dp[l+1][r-1]) == 1) {dp[l][r] = 1; continue;}
if(min(dp[l+2][r], dp[l+1][r-1]) == -1 && min(dp[l][r-2], dp[l+1][r-1]) == -1) {dp[l][r] = -1; continue;}

除此以外的情况,先置 \(dp_{l,r}=−1\) ,然后讨论Alice如何将其转变为平局或获胜:

  • 如果Alice选走 \(s_l\) 之后,\(dp_{l+1,r−1} 和 dp_{l+2,r}\) 中:

    • 不存在 −1 。

    • 不存在以下 Bob 能够获胜的情况:

      • \(s_r<s_l,dp_{l+1,r−1}=0\)
      • \(s_{l+1}<s_r,dp_{l+2,r}=0\)
  • 那么Alice至少为平局。

  • 更进一步地,如果Alice选走 sl 之后,dpl+1,r−1 和 dpl+2,r 中:

    • 不存在 −1 。
    • 不存在以下 Bob 能够能引向平局的情况:
    • \(s_r=s_l,dp_{l+1,r−1}=0\)
    • \(s_{l+1}=s_r,dp_{l+2,r}=0\)
  • 那么Alice将获胜。

对于 \(s_r\) ,有类似结论。

代码比较麻烦,容易写错。

#include<bits/stdc++.h>
using namespace std;
int f[2010][2010];//1:Alice 0:平局 -1:Bob 
int main()
{
	int t;
	cin>>t;
	while(t--){
		string s;
		cin>>s;
		int len=s.size();
		for(int i=0;i<len;i++) for(int j=0;j<len;j++) f[i][j]=0;
		for(int l=2;l<=len;l+=2)
		{
			for(int i=0;i<len;i++)
			{
				int j=i+l-1;
				if(l==2){
					if(s[i]==s[j]) f[i][j]=0;
					else f[i][j]=1;
					continue;
				}
				if(f[i+1][j-1]==1&&f[i+2][j]==1) {f[i][j]=1;continue;} //选左边后手(Bob)无论如何都是输 
				if(f[i+1][j-1]==1&&f[i][j-2]==1) {f[i][j]=1;continue;} //选右边后手(Bob)无论如何都是输 
				if(min(f[i+1][j-1],f[i+2][j])==-1&&min(f[i+1][j-1],f[i][j-2])==-1) {f[i][j]=-1;continue;} //选哪边后手(Bob)都有办法赢
				//剩下其余可能: 也就是说Alice不会必胜也不会必败,分类:Alice选左边或右边去试图追平或者取胜 
				f[i][j]=-1;//试图失败 
				//下面就是试图可能的情况 
				//选左边,且前面两人至少是追平的,有-1就不会选左边了 (因为正面情况太多所以考虑反面来))
				int flag=0; 
				if(min(f[i+1][j-1],f[i+2][j])==0)//保证没有-1,可能有1 
				{
					if(!(s[i]>s[j]&&f[i+1][j-1]==0)&&!(s[i]>s[i+1]&&f[i+2][j]==0)) //不存在Bob能取胜的情况
					{
						f[i][j]=0;
						if(!(s[i]==s[j]&&f[i+1][j-1]==0)&&!(s[i]==s[i+1]&&f[i+2][j]==0)) f[i][j]=1;//不存在Bob能追平的情况 
					} 
				} 
				
				//选右边,且前面至少是追平的,有-1就不会选右边 
				if(min(f[i+1][j-1],f[i][j-2])==0&&!flag)//保证没有-1,但是可能有1 
				{
					if(!(s[j]>s[i]&&f[i+1][j-1]==0)&&!(s[j]>s[j-1]&&f[i][j-2]==0))
					{
						f[i][j]=0;
						if(!(s[j]==s[i]&&f[i+1][j-1]==0)&&!(s[j]==s[j-1]&&f[i][j-2]==0)) f[i][j]=1;
					}
				} 
			}
		}
		if(f[0][len-1]==1) cout<<"Alice\n";
		else if(f[0][len-1]==0) cout<<"Draw\n";
		else cout<<"Bob\n";
	}
} 

这题有个更好理解的版本:

定义 \(dp[i][j]\) 为Alice在执行最优策略下的结局(1表示胜,0表示平,-1表示败)

显然先手想要的结果的优先级是 :\(1>0>-1\)

显然后手想要的结果的优先级是:\(-1>0>1\)

那么 Alice 就是对 dp 做max,Bob 就是对 dp 做min,这里思路跟 Rake it in 很像

然而每次选择无非有一下四种:

考虑转移:

无非就四种情况,只考虑前两次操作

  • 先手拿 \(i\) ,后手拿 \(j\) , 从 \(dp[i+1][j-1]\) 转移
  • 先手拿 \(i\) ,后手拿 \(i+1\) ,从\(dp[i+2][j]\) 转移

这两种情况后手的人有选择权它会尽量的拿小的,故需要取两者的 Min ,设为 val1 。

  • 先手拿 \(j\) ,后手拿 \(i\) , 从 \(dp[i+1][j-1]\) 转移
  • 先手拿 \(j\) ,后手拿 \(j−1\) , 从 \(dp[i][j-2]\) 转移

后两种情况分析方法一样,取两者的 Min , 设为 \(val2\) 。

那么显然 \(dp[i][j]=max(val1,val2)\) ,因为先手者想要拿大的。

#include<bits/stdc++.h>
using namespace std;
int f[2010][2010];//1:Alice 0:平局 -1:Bob 
string s;
int calc(int l,int r,int x,int y)//计算结果的函数:Alice选了x,Bob选了y 
{
	if(f[l][r]!=0) return f[l][r];//如果前面都是平局,那选什么不影响结果 
	if(s[x]<s[y]) return 1;
	if(s[x]==s[y]) return 0;
	return -1; 
} 
int main()
{
	int t;
	cin>>t;
	while(t--){
		cin>>s;
		int len=s.size();
		for(int i=0;i<len;i++) for(int j=0;j<len;j++) f[i][j]=0;
		for(int l=2;l<=len;l++)
		{
			for(int i=0;i+l-1<len;i++)
			{
				int j=i+l-1;
				if(l==2)
				{
					f[i][j]=(s[i]==s[j])? 0:1;
				}else{
					f[i][j]=max(min(calc(i+1,j-1,i,j),calc(i+2,j,i,i+1)),min(calc(i+1,j-1,j,i),calc(i,j-2,j,j-1)));
				}
			}
		} 
		if(f[0][len-1]==1) cout<<"Alice\n";
		else if(f[0][len-1]==0) cout<<"Draw\n";
		else cout<<"Bob\n";
	}
} 

标签:博弈,int,Alice,&&,Bob,平局,dp
From: https://www.cnblogs.com/Jayint/p/16707800.html

相关文章

  • 0-4 测试面试题_16合并两个排序数组_17tcp和udp_18单元集成系统验收回归_19测试和开发
    面试题(除个别外)及部分解析答案来自牛客网https://www.nowcoder.com/exam/interview/以下所述内容并不是百分之百正确,仅供参考。16手写代码:合并两个排序数组Merge1......
  • 浅析RDP攻击面
    浅析RDP攻击面目录浅析RDP攻击面抓取RDP连接日志获取RDP凭据DumpRDPCredentialsFromCredentialsDirectoryDumpRDPCredentialsFromsvchost.exe找到正确的进程创建......
  • 2022ICPC网络赛 L LCS-like Problem(DP 子序列自动机)
    LLCS-likeProblem(DP子序列自动机)题目:​ 给出两个串s,t。请找出一个最长的子序列\(s'\),使其与\(t\)的最长公共子序列长度不大于1。输出这个最长的长度。思路:​ 题目......
  • 力扣dp
    97.classSolution{public:boolisInterleave(strings1,strings2,strings3){intlen1=s1.length(),len2=s2.length(),len3=s3.length();......
  • dp(背包问题)
    1.0-1背包状态转移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i])  ------>压缩为一维 dp[j]=max(dp[j],dp[j-w[i]]+c[i])   逆向......
  • 使用docker-compose创建wordpress博客网站
    1.简述wordpress是一款开源的博客CMS,dockerhub上有着官方的容器镜像,使用docker能够很简单的创建一个wordpress站点,本文简要介绍了如何使用docker-compose来创建。2.......
  • poj2096 Collecting Bugs (概率DP)
    题目描述一个软件有s个子系统,会产生n种bug。某人一天发现一个bug,这个bug属于一个子系统,属于一个分类。每个bug属于某个子系统的概率是1/s,属于某种分类的概率是1/n。则每......
  • 【已解决】wordpress 修改固定链接 伪静态URL出现nginx 404错误
    一、站点设置 打开站点设置,选择伪静态,选择wordpress   二、wordpress设置打开wordpress后台,选择设置---》固定链接 选择一个你喜欢的格式点击保存 之......
  • wordpress固定链接+宝塔nginx配置伪静态访问
    一、站点设置 打开站点设置,选择伪静态,选择wordpress   二、wordpress设置打开wordpress后台,选择设置---》固定链接 选择一个你喜欢的格式点击保存 之......
  • dp线段树优化
    题目:PottedFlowerDescriptionThelittlecattakesoverthemanagementofanewpark.Thereisalargecircularstatueinthecenterofthepark,surroundedby......