【2008.06.08】
【TopCoder College Tour - WestChina】
Summary : 今天的题目其实很简单,可惜仍然是速度不够,最后一题想出算法来却来不及编写。看来想要在Div1混下去还是得提高coding熟练度。今天状态实在不好,500分那题没考虑清楚,后来Challenge的时候真想自己把自己给Cha了赚50分=。= 总的来说现在做250分的那道题基本上可以拿到220左右的分数了,现阶段目标是提高速度,争取能在500分的题目上拿400+的分数!
Problems:
250分题
【Problem Statement】
给出一个8x8棋盘
#.#.#.#.
.#.#.#.#
#.#.#.#.
.#.#.#.#
#.#.#.#.
.#.#.#.#
#.#.#.#.
.#.#.#.#
其中"B","W",".","#"分别代表黑棋,白棋,白格子,黑格子。行编号从1到8,列编号从A到H。题目给出棋子的位置,输出摆上棋子之后的棋盘样子。
【Solution】
250分的题目通常是用来热身的,直接模拟就行,代码能写多短就写多短。通常看看高手的程序能学到很多技巧,高手的程序那叫一个赏心悦目啊——简约而不简单!今天在250分的题目上我也简短了一把= v =
500分题
【Problem Statement】
有一个机器人,只会按照长度为N的指令序列进行移动,每次移动的指令为"U"(x,y)->(x,y+1)或者"R"(x,y)->(x+1,y),给出机器人初始坐标(RobotX,RobotY)和陷阱目标(TrapX, TrapY),求能让机器人走入陷阱的指令序列。(指令循环执行)若不存在这样的序列则输出"I'LL GET YOU NEXT TIME",若有多种答案则输出字符串比较最小的方案。
【Solution】
这道题麻烦就麻烦在特殊情况,像(RobotX,RobotY) == (TrapX,TrapY),RobotX==TrapX,RobotY==TrapY,这种特殊情况还算好处理的,在计算模的时候对于边界的处理总会让人很晕。于是这时候最好能找到一个能覆盖到所有特殊情况的写法,使得我们不需要去一一处理每个特殊情况。
解这道题的突破口在于循环次数上,如果我们知道这个N长度的指令循环将执行多少次,那么我们可以通过枚举指令序列中的U和R的个数来构造指令串。想到这一点问题就好办了,因为每次不管机器人向上还是向右,他总需要移动1的距离,我们只需要算出机器人与陷阱之间的街区距离(TrapX-RobotX)+(TrapY-RobotY)即可知道机器人要走进陷阱所需要的确切步数totstep,然后用circles = totstep / n求得整轮数,令rx,和ry表示机器人走了circle轮之后还需走多少步掉入陷阱,则rx = (TrapX - RobotX) - circles * nR; ry = (TrapY - RobotY) - circles *nU;此时我们只需要枚举nU(即N-nR)即可获得每种情况下的rx,ry,然后将rx个R放在最前面,接下来ry个U,再把剩下的R和U按顺序填上去,即可构成我们需要的串。最后在这些串里头找个最小的返回即可。
1000分题
【Problem Statement】
我们现在有N个能按照顺序显示字符串中字母('A'-'Z',且每个字符串中没有相同字符,字符串最大长度为26)的屏幕,若第i秒某个屏幕显示的为第k个字母,则i+1秒显示的为第k+1个字母(遇到末尾返回到首个)。在第0秒所有的屏幕显示的均为每个字符串的第一个字母,现在给出这些字符串以及希望看到的单词,问最少多少秒后能看到希望看到的单词,无解则输出-1。
Example:
{"XYZ", "DEF", "OPRS"}
"YES"
Returns: 7
The message displayed at each second is:
0: XDO
1: YEP
2: ZFR
3: XDS
4: YEO
5: ZFP
6: XDR
7: YES
【Solution】
设第i个字符串的长度为len(i),编号从0开始。
我最开始的想法是先找到第0个屏幕出现所需字母的时间k,然后每次增加incr = len(0)依次检查其他未出现要求字母的屏幕,若j号屏幕在k+len(0)出现的为所求的字符,则接下来每次增量incr改成incr和len(j)的最小公倍数,依次类推,直到所有的屏幕均满足条件或超出所有字符串长度最小公倍数为止。但是这样每次都扫描一遍所有的字符串太麻烦,后来发现可以改成按照顺序求,即:从0~N-1,每一步(i)都求出第0到第i个屏幕上的显示均符合要求的最小时刻,每找到一个,则更新incr为前i+1个字符串长度的最小公倍数。由于这个最小公倍数增长得十分快,所以很快就会找到答案或者超出所有字符串长度的最小公倍数,因此时间效率并不是问题。不过在测试的时候我还是有个小地方出了问题:在该用long long的时候只用了int,结果导致输出了负数,实在是不应该。
看到某大牛写的相当简练的求最大公约数代码:
【The End】
vector <string> TextBoard::draw(vector <string> pieces) { vector<string> vs(8, "########"); for (int i=0; i<8; ++i) for (int j=0; j<8; ++j) vs[i][j] = (i+j)%2 ? '.' : '#'; for (int i=0; i<pieces.size(); ++i) { int x = pieces[i][7] - '1'; int y = pieces[i][6] - 'A'; vs[x][y] = pieces[i][0]; } return vs; }
string EvilRobot::createProgram(int N, int robotX, int robotY, int trapX, int trapY) { int dx = trapX - robotX; int dy = trapY - robotY; int tot = dx + dy; int t = tot / N; if (dx<0 || dy<0) return "I'LL GET YOU NEXT TIME"; string ans; for (int nU = 0; nU<=N; ++nU) { int nR = N - nU; int rx = dx - nR * t; int ry = dy - nU * t; //printf("nU=%d, nR=%d, rx=%d, ry=%d/n", nU, nR, rx, ry); if (rx < 0 || ry < 0 || rx > nR || ry > nU) continue; string s; for (int i=0; i<rx; ++i) s+='R'; for (int i=0; i<ry; ++i) s+='U'; for (int i=0; i<nR-rx; ++i) s+='R'; for (int i=0; i<nU-ry; ++i) s+='U'; //printf("s=%s ans=%s/n", s.c_str(), ans.c_str()); if (ans == "" || s<ans) ans = s; } return ans == "" ? "I'LL GET YOU NEXT TIME" : ans; };
long long RollingLetters::gcd(long long a, long long b) { return (b ? gcd(b, a%b) : a); } long long RollingLetters::getTime(vector <string> reels, string requiredText) { long long ans = 0; long long mod = 1; for (int i=0; i<reels.size(); ++i) { int j; for (j=0; j<reels[i].length(); ++j) { if (reels[i][j] == requiredText[i]) break; } long long cntm = reels[i].length(); int suc = false; for (long long k = mod; k < (1ULL<<63) && k / mod <= cntm; k += mod) { if ((ans + (k-mod)) % cntm == j) { suc = true; ans += (k-mod); mod *= cntm / gcd(mod, cntm); break; } } if (!suc) return -1; } return ans; }