前一段英雄会的有奖题目,貌似有三千多人参加,通过率三十分之一。
当时看到题目的时候正好空闲,被奖品诱惑了,花了半个小时思考,半个小时编码,测试用例一次通过。
后来,结果出来了,没有后来了。所以我自夸最快的解,只能算是吸引眼球。
后来我看到几个网上的解题思路,都没有这个简单,不少人都用了复杂的数据结构;
还看到有人说他的程序运行时间在毫秒级,我心想,呃,太慢了吧……
哥也贴代码出来吧,至少不枉费我花的一个多小时啊。
题目详情
甲乙两个人用一个英语单词玩游戏。两个人轮流进行,每个人每次从中删掉任意一个字母,
如果剩余的字母序列是严格单调递增的(按字典序a < b < c <....<z),则这个人胜利。
两个人都足够聪明(即如果有赢的方案,都不会选输的方案 ),甲先开始,问他能赢么?
输入: 一连串英文小写字母,长度不超过15,保证最开始的状态不是一个严格单增的序列。
输出:1表示甲可以赢,0表示甲不能赢。
例如: 输入 bad, 则甲可以删掉b或者a,剩余的是ad或者bd,他就赢了,输出1。
又如: 输入 aaa, 则甲只能删掉1个a,乙删掉一个a,剩余1个a,乙获胜,输出0。
函数头部:
C:int who (char * word);
C++:int who (string word);
Java:public static int who(String in);
C# :public static int who(string word);
思路:长度不超过15的字符串,甲乙轮流取走,用位标记取走的情况,
一个unsigned short就可以表示搜索策略,一个容量为65536的数组记录搜索结果。
这样,理论上递归的次数不会超过2^15,平均运行时间应该在纳秒和微秒级。
递归搜索的描述:
如果是甲在取字符,只要找到一个可以赢的取法,甲即可获胜;如果试过所有的取法都不能赢,那么甲就不能获胜。
如果是乙在取字符,只要找到一个可以赢的取法,那么甲即不能获胜;如果试过所有的取法都不能赢,那么甲就可以获胜。
代码如下,核心函数36行。
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;
class Test {
public:
static int who (string word)
{
Test test( word );
return test.can_win( 0, true );
}
Test( string& word )
{
_word = word.data();
_len = word.size();
memset( _check, -1, 1 << _len );
}
char can_win( unsigned short now, bool self )
{
char last = 0;
for( int i = 0; i < _len; i++ ){
if( !( now & ( 1 << i ) ) ){
if( _word[i] <= last ){
last = 0;
break;
}
last = _word[i];
}
}
if( last ){
return self ? 0 : 1;
}
for( unsigned short get = 1; get != ( 1 << _len ); get <<= 1 ){
if( now & get ){
continue;
}
unsigned next = now | get;
char win = _check[ next ];
if( win < 0 ){
win = can_win( next, !self );
_check[ next ] = win;
}
if( self ){
if( win ){
return 1;
}
}else{
if( !win ){
return 0;
}
}
}
return self ? 0 : 1;
}
private:
const char* _word;
int _len;
char _check[65536];
};
//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int main()
{
cout<<Test::who("aaaaa")<<endl;
}
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。
吐个槽:英雄会的评奖都是完全黑箱操作,连瞻仰一下最终获奖者的代码的途径都没有。