昨天把在线编程的第二道题提交了,所以,今天有空坐下来写写第一道题的答案。
逻辑思路:
1、如果甲要取胜,那么对甲来说,面对的字符串,划掉1个字符,必然胜利。
2、如果划掉一个字符,甲不能确定取胜,那么甲有N种划法,假设现在甲面对的字符串长度为N。比如:abcab,那么甲有5种划法,分别是划掉第一个,第二个.......字符。如果甲想取胜,那么,至少有一种划法,划掉一个字符后,交给乙,乙必然失败才行。
3、对于甲划掉的字符,交给乙,对乙来说,面对和甲相同的判断,及需要判断1,2步骤。
由上面分析,可以看出,一个简单的递归就可以解决了。下面开写。
首先写第一个函数:
判断字符串,划掉某一个字符,是不是必然胜利
private bool winCheck(string word)
{
bool flag = false;
int count = 0;
char[] arrword = word.ToCharArray();
int start = -1;
for (int i = 1; i < word.Length; i++)
{
if (arrword[i] > arrword[i - 1])
{
}
else
{
count++;
if (count >= 2)
{
break;
}
else
{
start = i - 1;
}
}
}
if (count >= 2)
{
flag = false;
return flag;
}
else
{
if (count == 0)
{
flag = true;
}
else
{
if (start == 0 || start == word.Length - 2)
{
flag = true;
}
else
{
if (arrword[start + 1] > arrword[start - 1] || arrword[start + 2] > arrword[start])
{
flag = true;
}
else
{
flag = false;
}
}
}
}
return flag;
}
这个函数逻辑很简单,就是循环以下这个字符串,看看划掉某一个字符串是不是形成一个单调递增的序列。
第一步的判断函数写好了。
第二步,由于甲和乙的判断逻辑一样,所以代码可以整合,参数就是word,表示需要判断的字符串,who表示是谁对当前字符串进行判断。
这个判断的逻辑就是:
1、判断当前字符串是否能够必胜。
2、不能够,则遍历所有可能字符串长度N种可能,只要有一种可能,保证自己必胜,那么就划掉这个字符。
private int CheckAll(string word, int who)
{
int result = 0;
if (winCheck(word))
{
result = who;
}
else
{
for (int i = 0; i < word.Length; i++)
{
string tmp = word.Substring(0, i) + word.Substring(i + 1);
int nextwho = who == 0 ? 1 : 0;
result = CheckAll(tmp, nextwho);
//划掉这个字符,对手必输,所以,不用再往下循环了
if (result == who)
{
break;
}
}
}
return result;
}
ok,到此,所有逻辑完成,看看代码,真的还是比较简洁的。
提交,运行,结果是出来了,不过时间是超过3s了。
看来还需要再优化。
问题来了,优化哪里呢?将递归调用转换成迭代,但是分析了下,次数并不会减少啊。
看来此路不通啊。
winCheck函数只循环了一次,看看,问题不在这里。
问题出在递归调用上,浪费了大量时间。
再分析递归调用的逻辑,当一个人划掉一个字符后,交给另一个人去判断,会不会出现很多次,即之前这个字符串已经判断过,并确定了一定是某人胜利?简单分析后,发现出现频率很高,意味着做了很多重复无用功,因此,用一个变量来存储。
我们就选用private Dictionary<string, int> Dic;来存储,字符串,int表示谁胜利,这里还有一个简单的判断,即甲和乙不可能遇到一模一样的字符串(为什么?交给大家想想)
改造下CheckAll函数:
private int CheckAll(string word, int who)
{
int result = 0;
if (winCheck(word))
{
result = who;
if (!Dic.ContainsKey(word))
{
Dic.Add(word, who);
}
}
else
{
for (int i = 0; i < word.Length; i++)
{
string tmp = word.Substring(0, i) + word.Substring(i + 1);
int nextwho = who == 0 ? 1 : 0;
if (Dic.ContainsKey(tmp))
{
result = Dic[tmp];
}
else
{
result = CheckAll(tmp, nextwho);
if(!Dic.ContainsKey(tmp))
{
Dic.Add(tmp, result);
}
}
if (result == who)
{
break;
}
}
}
return result;
}
现在再运行,输入15位的随机字符串,呵呵,都是0.01秒左右,蛮好,完成。