Problem地址:http://acm.hdu.edu.cn/showproblem.php?pid=1404
刚开始想采取找规律的方法解题,可以没有发现规律。无奈,只好采用求PN点的方法。
我们假设数字最右边为第一位,且为最低位。那么可以知道当最高位为0时,那么先手的人必定能胜利,这个可以作为一个剪枝条件。
接下来就是两种操作了:1.降低其中任何一位
2.把其中一个0及这位0后面所有的数字删去
我们只需通过这两个操作,寻找目标数字的后续数字状态的PN状态,即可求得目标数字的PN状态。其实我们只需判断后续状态中有没有P点就行了,如果有P点,那么目标数字的状态为N点。如果没有N点,那么目标数字的状态为P点。通过这方法,就可以求得题目要求数字的PN状态了。
#include <iostream> #include <cstring> #include <sstream> #include <cstdio> using namespace std; const int MAXN = 1000000 + 5; int sg[MAXN]; stringstream ss; int length ; inline int Get_digit( int num, int i ) // 求出数字第i位数,最右边为第1位 { switch (i){ case 1: return num%10; case 2: return num%100/10; case 3: return num%1000/100; case 4: return num%10000/1000; case 5: return num%100000/10000; default : return num/100000; }; } int Get_sg( int num ) // 求sg值 { if( sg[num]!=-1 ) return sg[num]; int temp = 1; int orignal = num; int i; for( i=0;i<length-1;i++ ) // 缩小第i位的值 { num = orignal; if( Get_digit( num, i+1 )==0 ) { // 消去0即后面所有的数 if( Get_sg( num/( temp*10 ) )==0 ) // 发现必败点 { sg[ orignal ] = 1; // 则此点为必胜点 return 1; } } num = orignal; while( Get_digit( num, i+1)!=0 ) { num = num - temp; if( Get_sg( num )==0 ) // 发现必败点 { sg[ orignal ] = 1; // 则此点为必胜点 return 1; } } temp = temp * 10; } num = orignal; while( Get_digit(num, length)>1 ) // 最高位单独处理 { // 避免出现 类似于 123 -> 23 的情况 num = num - temp; if( Get_sg( num )==0 ) // 发现必败点 { sg[ orignal ] = 1; // 则此点为必胜点 return 1; } } sg[orignal] = 0; // 没有发现必败点,则此点即为必败点 return 0; } int main() { memset( sg, -1,sizeof(sg) ); // 初始化 char in[10]; int num, ans; sg[0] = 1; sg[1] = 0; // 初始化 while( scanf("%s", in)!=EOF ) { if( in[0]=='0' ) // 最高为为0, 则一定胜利 { printf( "Yes " ); continue; } ss.clear(); ss << in; ss >> num; // char转int length = strlen(in); // 求该数字的长度 ans = Get_sg( num ); if( ans ) { printf( "Yes " ); } else { printf( "No " ); } } return 0; }