思路
博弈题目,可以使用递归,如果本轮先手做完某个操作后,可以使下一轮的操作人必败,则此轮做这个操作可以使先手必胜。相应地,如果本轮无论做哪种操作都无法使下一轮的人必败(即下一轮的人有策略必胜),则当前状态必败。所以可以用int _opt(int n,int x,int y)
作为这个递归函数,如果有必胜策略返回操作编号,如果没有返回
−
1
-1
−1。终止状态就是
x
+
y
≥
n
x+y\ge n
x+y≥n,此时只能做完操作
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
2i⋅3j(
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(0≤i<15,0≤j<10,0≤k≤30000) 表示
x
=
2
i
⋅
3
j
,
y
=
k
x=2^i·3^j,y=k
x=2i⋅3j,y=k 的_opt
返回值。
代码
关于这道交互题:
- 如果用了
pair
这类东西,要写一下万能头。 - 内存超限或是什么其他错导致的 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];
}
题外话,本人有写过两篇专门讲博弈的文章,想要深入学习可以看看。