P6612 [POI2012] LIC-Bidding(记忆化搜索+博弈)

原题链接

思路

博弈题目,可以使用递归,如果本轮先手做完某个操作后,可以使下一轮的操作人必败,则此轮做这个操作可以使先手必胜。相应地,如果本轮无论做哪种操作都无法使下一轮的人必败(即下一轮的人有策略必胜),则当前状态必败。所以可以用int _opt(int n,int x,int y)作为这个递归函数,如果有必胜策略返回操作编号,如果没有返回 − 1 -1 1。终止状态就是 x + y ≥ n x+y\ge n x+yn,此时只能做完操作 1 1 1 之后输掉游戏,返回 − 1 -1 1
但是这样会把同一个状态多次走到并且一直递归到底,超时。所以考虑使用 r e c i , j rec_{i,j} reci,j 记录 x = i , y = j x=i,y=j x=i,y=j 时的_opt返回值,可是 30000 × 30000 30000\times30000 30000×30000 太大。观察一下操作, x x x 要么变成 1 1 1,要么 × 2 \times2 ×2 × 3 \times3 ×3,所以 x x x 一定是这种形式: 2 i ⋅ 3 j 2^i·3^j 2i3j x x x 的初始值是 1 1 1,任何一个状态的 x x x 都是从 1 1 1 乘上来的)。所以可以用 r e c i , j , k ( 0 ≤ i < 15 , 0 ≤ j < 10 , 0 ≤ k ≤ 30000 ) rec_{i,j,k}(0\le i<15,0\le j<10,0\le k\le 30000) reci,j,k(0i<15,0j<10,0k30000) 表示 x = 2 i ⋅ 3 j , y = k x=2^i·3^j,y=k x=2i3j,y=k_opt返回值。

代码

关于这道交互题:

  1. 如果用了pair这类东西,要写一下万能头。
  2. 内存超限或是什么其他错导致的 RE,会判成“You tried to hack”。
#include<bits/stdc++.h>
using namespace std;
short rec[15][10][30005];
int cnt;
pair<int,int> change(int x){//把 x 转化为 i,j 的形式
	pair<int,int> p=make_pair(0,0);
	while(x%2==0){
		x/=2;
		p.first++;
	}
	while(x%3==0){
		x/=3;
		p.second++;
	}
	return p;
}
extern "C" int _opt(int n,int x,int y){
	pair<int,int> p=change(x);
	if(x+y>=n) return -1;
	if(rec[p.first][p.second][y])
		return rec[p.first][p.second][y];
	int flag1=_opt(n,1,x+y),flag2=_opt(n,2*x,y),flag3=_opt(n,3*x,y);
	if(flag1==-1) rec[p.first][p.second][y]=1;
	else if(flag2==-1) rec[p.first][p.second][y]=2;
	else if(flag3==-1) rec[p.first][p.second][y]=3;
	else rec[p.first][p.second][y]=-1;
	return rec[p.first][p.second][y];
}

题外话,本人有写过两篇专门讲博弈的文章,想要深入学习可以看看。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值