好长一段时间没写博客了,实在是想不出有什么好写的。近期也有对自己的职业做了一点思考,还是整理不出个所以然来,很是烦躁 ...
研究TopCoder已经有一小段时间了,都是在做之前的题目,还没有实际参加过比赛。以后应该会开设一个topcoder的分类专门写一些里面的一些题目,就当做笔记来写 ...
比较喜欢这种解题的方式,有一些题目还是很有挑战性的,这次先贴出一题做一下暖身。(SRM 594 DIV 2)
1、这是道250分的题,原题是:
Fox Ciel is now in high school. The seats in her classroom are arranged into an n by m matrix. The rows are numbered from 0 to n-1 (front to back) and the columns from 0 to m-1 (left to right).
At the beginning, Ciel can choose any of the seats. Then, at the end of each week Ciel will shift one row to the back and one column to the right, wrapping around whenever necessary. Formally, if her current seat is in row r and column c, then her seat next week will be the one in row ((r+1) modulo n) and column ((c+1) modulo m).
Fox Ciel now wonders whether she can sit in all the seats in the classroom if she follows the above procedure. As we already mentioned, she can start in any of the seats. Also, she can attend the school for as many weeks as she wants to. Return "Possible" if she can sit in all the seats and "Impossible" otherwise.
请原谅我蹩脚的英语水平,我只能偷偷地打开Google翻译一下,大致理解一下应该是以下的意思:
某个家伙在上高中,教室里有n×m个座位,编号从0开始。
这货一开始可以随便选一个座位,假设为(x, y),一个星期后,他就要换到现在座位的右后方一个位置,即(x+1, y+1),如果此时他正好坐在最边上,那就从对边的地方开始,即如果x+1=n => x=0; 如果y+1=m => y=0; 换成计算公式就是((x+1)%n, (y+1)%m)。
假设这货还有nnnnnnnn个星期要上课(好悲剧的说),那么在给定的n, m下,请问他是否有可能坐完所有座位(真够蛋疼的)。
首先先将题目抽象一下,根据题意,可以知道:
1、根据地球是圆的,绕一圈后会回到原点这个定律(-_-! 哪来的定律?有毛线关系?),这家伙无论一开始选择什么座位,到最后还是会回到这个位置,所以可以忽略"选择第一个座位"的问题。
2、程序结束的标志是,他回到原来的位置时,已经坐完所有座位,or 还没坐完所有座位。
题目另外给出了参数n、m的值范围是:2-10;
测试数据:
n=2, m=3, result = Possible
n=2, m=2, result = Impossible
n=4, m=6, result = Impossible
n=3, m=6, result = Impossible
n=5, m=7, result = Possible
n=10, m=10, result = Impossible
最暴力的做法是,一个大循环,当回到第一个位置时,判断是否已经遍历了所有位置。
用一个list模拟保存已坐过的位置,根据题目给的公式计算:
var count = n * m; var list = new List<string>(); int x = 0, y = 0; while (true) { var position = string.Format("{0}:{1}", x, y); if (list.Contains(position)) return list.Count == count ? "Possible" : "Impossible"; else list.Add(position); x = (x + 1) % n; y = (y + 1) % m; }
一开始看到这个题目和测试数据后,想到的是另一种可能:n、m值之间的关系直接决定了结果。
于是就画了图,逐个逐个做了比较分析,最后得出了结论:只要n、m无公约数,就可以!
for (int i = 2, maxDivisor = Math.Min(n, m); i <= maxDivisor; ++i) if (n % i == 0 && m % i == 0) return "Impossible"; return "Possible";
暴力方法比较直观,容易理解,时间复杂度为O(nm);第二种方法,时间复杂度为O(min(n, m)),逻辑明显更简单。
如果抛开这道题的限制,第二种方法无疑是比暴力方法快了很多很多很多很多倍,分别测过其他几份较大的数据,结果也是一样的: