292题:Nim Game
不难看出递推式:
num[i]表示有i个石子时能不能赢。堆中有i个石子时,赢的条件是i-1、i-2和i-3个石子有一种不能赢,即能找到一种对方失败的拿法即可。
纸上写几个就会发现结果的周期为4,是4的倍数就会输,其他都能赢。
所以写出程序。
bool canWinNim(int n) {
if (n % 4 == 0)
return false;
else
return true;
}
319题:Bulb Switcher
手推几个,发现第i个灯最后的开关取决于i在[2 - i / 2]中因数的个数是奇数还是偶数。
于是采用筛选法求素数的思想,从2~i / 2把它的倍数都打上标记,结果发现超时,代码如下:
int bulbSwitch(int n) {
bool* num = new bool[n + 1]();
num[1] = 1;
for (int i = 2; i <= n / 2; i++) {
int c = i + i;
while (c <= n) {
num[c] = !num[c];
c += i;
}
}
int res = 0;
for (int i = 1; i <= n; i++) {
if (num[i])
res++;
}
return res;
}
因此O(N)的算法是行不通的,那只能再找规律了。再手推几个,发现如果[2 - i / 2]中有一个因数,那么另一个因数也在这个区间,所以抵消了。有奇数个因数出现的情况只能是这个数是某个数的平方数。所以问题变成1~n中有几个平方数,即对n开根号,可以采用O(logN)的二分查找法。但是我懒得写了,直接调用sqrt。
int bulbSwitch(int n) {
return sqrt(n);
}
777题:Swap Adjacent in LR String
一开始看错题目,以为X可以和左右的'L'或'R'互换,写完提交后才发现只有'RX'->'XR'和'XL'->'LX'两种换法,审题啊,审题,面Google的时候题目都没看完就开始做了,我也是服了我自己T_T。
我的解法比较蠢,用x_num记录end比start中多几个X,然后用一个指针指向当前比较的位置。写了一堆判断,主要是判断R和L不能交错,即R可以和X互换从而向右移,L可以和X互换从而向左移,但是R是不可能跑到L右边的(因为R和L不能互换)。
如果两者相等,下一位。
如果start中是R的话,那么end中该位置只能是'X',而且x_num要>=0,即之前没有过多余的L。
如果start中是L的话,那么end中该位置只能是'X',而且x_num要<0,即之前有多余的X。
如果start中是X的话,那么根据end中该位置是'L'还是'R'分别判断。
bool canTransform(string start, string end) {
int len = start.size();
int x_num = 0;
for (int i = 0; i < len; i++) {
if (start[i] == end[i])
continue;
if (start[i] == 'R' && end[i] == 'X' && x_num >= 0) {
x_num++;
}
else if (start[i] == 'X') {
if (x_num > 0 && end[i] == 'R') {
x_num--;
}
else if (x_num <= 0 && end[i] == 'L') {
x_num--;
}
else
return false;
}
else if (start[i] == 'L' && end[i] == 'X' && x_num < 0) {
x_num++;
}
else
return false;
}
if (x_num)
return false;
return true;
}
然后看了discussion,发现有解法用两个标记分别指向start和end中的位置,如果是X直接下一个,直到两者都不是'X'然后判断是否相等,以及此时的位置不是偏左(start[i]==R)或偏右(start[i]==L)。比我的解法简单粗暴,而且不容易写错Orz。