题目大意:有两个人玩游戏,每次对a或b进行加1的操作,使得a^b<n,若不可操作则为负,先判断是否平局,若有必胜手段,输出负者
解题思路:博弈类型动态规划,这里采用的是记忆化搜索,没有接触过此类问题的可以先做一下UVAOJ的10404,题目链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1345
在此题中,为了叙述方便,将stas设为第一人称我,则stas赢即为“我赢”,反之为“我负”
接下来说一下这道题目中的几个坑点:
首先是判断是否为平局,a=1就一定为平局?no,只有当a=1且如果我让a=a+1时,我没有胜利的策略时才让b=1,对手亦然,直到不能让a+1(即(a+1)^b>=n,或者b>40,因为2^40差不多也会超过10^9,这里很容易漏)时才是平局。
接着就是考虑a^b的计算,显然要用快速幂(直接取对数只有在初始的时候可以用来计算,否则会因为精度问题卡到死),又初始的时候a,b显然可能直接超过n,所以需要取对数特判。
接着就是b=1的情况,这种情况按理应该不会有什么问题,但是仔细细想一下如果双方分别让a=a+1则会超时(因为让b=b+1)没有必胜策略啊!那这时就应该通过判断单调性来直接优化!
AC代码:
#include <iostream> #include <string.h> #include <cmath> #include <algorithm> using namespace std; typedef long long LL; int dp[100010][70]; LL a,b,n; LL pow_mod(LL a,LL b) { if(b == 0) return 1; LL x = pow_mod(a,b>>1); LL ans = x*x; if(b&1) ans = ans*a; return ans; } int dfs(LL a,LL b) { if(pow_mod(a,b)>=n) return 0; if(dp[a][b] != -1) return dp[a][b]; if(a == 1) { if(b>=40 || b*b>=n) return dp[a][b] = 2; if(dfs(a+1,b) == 1) return dp[a][b] = 0; if(dfs(a,b+1) == 0) return dp[a][b] = 1; if(dfs(a,b+1) == 2) return dp[a][b] = 2; } if(b == 1) { for(int i = 0;(i+a)*(i+a)<n;i++) { if(i % 2 == 0 && dfs(i+a,2) == 1) return dp[a][b] = 0; if(i % 2 == 1 && dfs(i+a,2) == 1) return dp[a][b] = 1; } if((n-a) % 2 == 1) return dp[a][b] = 1; else return dp[a][b] = 0; } if(dfs(a+1,b) == 1 || dfs(a,b+1) == 1) return dp[a][b] = 0; else return dp[a][b] = 1; } int main() { memset(dp,-1,sizeof(dp)); cin >> a >> b >> n; if(b*log(a)>=log(double(n))) cout << "Stas" << endl; else if (dfs(a,b) == 0) cout << "Masha" << endl; else if (dfs(a,b) == 2) cout << "Missing" << endl; else if (dfs(a,b) == 1) cout << "Stas" << endl; return 0; }