牛牛在玩一个游戏,他操纵的游戏角色现在在一个坐标系的(0,0)处,他每次操纵该游戏角色都只能使它往上走或者往右走,向上走记为'U',向右走记为'R'。在这个坐标系中散落着n张卡片,当该游戏角色移动到卡片的位置上时即可收集到这张卡片。
游戏胜利的条件是:收集到所有的卡片。
现在,牛牛想知道他能否赢得游戏,所以他想请聪明的你帮他写一个程序,如果可以赢得游戏,请返回移动的路径序列(例如:"RURURUR"),如果不可以,请返回"NO"。当然,如果有多条路径都可以赢得游戏,请输出字典序最小的一条。
时间复杂度 空间复杂度
解法:我们可以利用一个pair或者结构体存储卡片的坐标,然后进行排序,排序的x与y的优先级任意,因为下一张卡片一定在当前游戏角色位置右方/上方/右上方,否则直接输出NO,因为不可能存在这种移动方式。
在输出移动序列时,为了使字典序最小,所以能先用'R'就用'R',因为'U'的字典序比'R'大。
按照上述思路进行模拟即可,我这里使用的pair,代码如下:
typedef pair<int, int> P; string add(int up, int right, string ans) { int i; for (i = 0; i < right; i++) ans += "R"; for (i = 0; i < up; i++) ans += "U"; return ans; } /** * 如果可以赢得游戏,返回字典序最小的序列,反之返回"NO" * @param n int整型 卡片的数量 * @param a int整型vector<vector<>> 卡片的坐标数组 * @return string字符串 */ string solve(int n, vector<vector<int>> &a) { // write code here int i; P p[10050]; //使用pair默认先x再y p[0] = P(0, 0); string ans = ""; for (int i = 0; i < n; i++) { p[i + 1].first = a[i][0]; p[i + 1].second = a[i][1]; } sort(p + 1, p + 1 + n); for (i = 1; i <= n; i++) { if (p[i].first > p[i - 1].first && p[i].second < p[i - 1].second) { return "NO"; } ans = add(p[i].second - p[i - 1].second, p[i].first - p[i - 1].first, ans); } return ans; }
写法还可以优化,可以不开额外的数组,在string的尾部添加char,可以使用append。
由于只能向右和向上走,所以将坐标按x值排序,然后按y排成非递减的序列,如果不能排成则肯定不存在路径,然后就贪心地构造就行了,要构成字典序最小,所以先向右走,再向上走(‘R’<'U')
时间复杂度 空间复杂度
class Solution { public: /** * 如果可以赢得游戏,返回字典序最小的序列,反之返回"NO" * @param n int整型 卡片的数量 * @param a int整型vector<vector<>> 卡片的坐标数组 * @return string字符串 */ string solve(int n, vector<vector<int> >& a) { // write code here a.push_back({ 0,0 }); sort(a.begin(), a.end(), [](vector<int>& a, vector<int>& b) { return a[0] != b[0] ? a[0] < b[0] : a[1] < b[1]; }); string ret = ""; int i = 0; for (i + 1; i + 1 < n && a[i+1] == vector<int>{0, 0}; i++); for (i++,n++; i < n; i++) if (a[i][1] < a[i-1][1]) return "NO"; else { ret.append(a[i][0] - a[i - 1][0], 'R'); ret.append(a[i][1] - a[i - 1][1], 'U'); } return ret; } };
感谢审题人@张领统提供的另一份时间复杂度O(n),空间复杂度O(n)的解法:
class Solution { public: /** * 如果可以赢得游戏,返回字典序最小的序列,反之返回"NO" * @param n int整型 卡片的数量 * @param a int整型vector<vector<>> 卡片的坐标数组 * @return string字符串 */ string solve(int n, vector<vector<int>> &a) { vector<int> cnt(10005, 0); vector<int> mn(10005, 1e8); vector<int> mx(10005, -1); for (auto &p : a) { cnt[p[0]]++; mn[p[0]] = min(p[1], mn[p[0]]); mx[p[0]] = max(p[1], mx[p[0]]); } int prev = 0; int flag = 1; for (int i = 0; i < 10005; i++) { if (cnt[i] == 0) continue; if (mn[i] < prev) { flag = 0; break; } prev = mx[i]; } if (!flag) return "NO"; int prevx = 0, prevy = 0; string ans = ""; for (int i = 0; i < 10005; i++) { if (cnt[i] == 0) continue; for (int j = prevx; j < i; j++) ans += 'R'; for (int j = prevy; j < mx[i]; j++) ans += 'U'; prevx = i; prevy = mx[i]; } return ans; } };