题目:
给你一个整数 n ,请你找到满足下面条件的一个序列:
- 整数 1 在序列中只出现一次。
- 2 到 n 之间每个整数都恰好出现两次。
- 对于每个 2 到 n 之间的整数 i ,两个 i 之间出现的距离恰好为 i 。
序列里面两个数 a[i] 和 a[j] 之间的 距离 ,我们定义为它们下标绝对值之差 |j - i| 。
请你返回满足上述条件中 字典序最大 的序列。题目保证在给定限制条件下,一定存在解。
一个序列 a 被认为比序列 b (两者长度相同)字典序更大的条件是: a 和 b 中第一个不一样的数字处,a 序列的数字比 b 序列的数字大。比方说,[0,1,9,0] 比 [0,1,5,6] 字典序更大,因为第一个不同的位置是第三个数字,且 9 比 5 大。
示例 1:
输入:n = 3
输出:[3,1,2,3,2]
解释:[2,3,2,1,3] 也是一个可行的序列,但是 [3,1,2,3,2] 是字典序最大的序列。
示例 2:
输入:n = 5
输出:[5,3,1,4,3,5,2,4,2]
提示:
- 1 <= n <= 20
来源:
解题思路:回溯
定义一个数组used,记录某个数字是否已使用,用于回溯。一个数组path,长度2*n-1,记录结果。递归方向与path一致,每次递归从最大的数N开始依次判断是否满足条件,保证结果字典序最大。
- 结果满足条件:path遍历完成,每个元素都不为0。
- 递归调用分两种:
- 数字1:数字1只有1个,不用考虑距离
- 数字2~N:每个数字出现2次且距离为当前数字,所以根据path[start]当前位置找出符合条件的数字,然后递归回溯。
- start变量:递归内指向path,当path[start]已有数字时,跳过。
class Solution {
public:
vector<int> result;
bool ok;
vector<int> constructDistancedSequence(int n) {
ok = false;
vector<int> path(n*2 - 1, 0);
vector<bool> used(n+1, false);
back(path, used, n, 0);
return result;
}
// 选择一个数字放到path[start], path[start],path[start+i]可能已放置,需要判断
// start从0开始直到n*2-1
void back(vector<int>& path, vector<bool>& used, int n, int start) {
if (start == path.size()) {
result = path;
ok = true;
return;
}
if (path[start] > 0) {
back(path, used, n, start+1);
return;
}
// 优先选择较大的数字放到path[start]
for (int i = n; i >= 1; i--) {
if (ok) break;
if (used[i]) continue;
used[i] = true;
if (i == 1) {
path[start] = 1;
back(path, used, n, start+1);
path[start] = 0;
} else {
if (start + i < path.size() && path[start + i] == 0) {
// 满足条件的2个位置都是空
path[start] = i;
path[i+start] = i;
back(path, used, n, start+1);
path[start] = 0;
path[i+start] = 0;
}
}
used[i] = false;
}
}
};
后记:
今天被这题困扰了很久,先前写的代码总是超时,下面贴出当时的代码,并指出其中的问题。
问题一:n从1开始,则需要将所有结果都判断一次才能找出字典序最大结果,而且back被循环调用,所以超时。
问题二:递归的是数字N,遍历path找出位置,但这样找出的结果不是最大的,所以应该为:递归path,遍历数字N。
class Solution {
public:
vector<int> result;
vector<int> constructDistancedSequence(int n) {
vector<int> path(n*2 - 1, 0);
result = path;
// 将数字1放入每一个位置,然后递归+回溯
for (int i = 0; i < path.size(); i++) {
path[i] = 1;
back(path, n, 2);
path[i] = 0;
}
return result;
}
// start从2开始直到n+1
void back(vector<int>& path, int n, int start) {
if (start == n + 1) {
if (greater(path, result)) {
result = path;
}
return;
}
for (int i = 0; i < path.size(); i++) {
if (path[i] == 0 && i+start < path.size() && path[i+start] == 0) {
// 满足条件的2个位置都是空
path[i] = start;
path[i+start] = start;
back(path, n, start+1);
path[i] = 0;
path[i+start] = 0;
}
}
}
bool greater(const vector<int>& a, const vector<int>& b) {
for (int i = 0; i < a.size(); i++) {
if (a[i] > b[i]) {
return true;
} else if (a[i] < b[i]) {
return false;
}
}
return false;
}
};