题目地址:
https://leetcode.com/problems/construct-the-lexicographically-largest-valid-sequence/
给定一个正整数
n
n
n,要求设计一个数组满足以下条件:
1、
1
1
1只出现
1
1
1次;
2、
2
∼
n
2\sim n
2∼n这些数出现
2
2
2次,并且两次
x
x
x出现的位置之间的距离恰好是
x
x
x。
题目保证有解,输出字典序最大的解。
直接暴力枚举。在DFS的时候,枚举当前位置应该填谁,由于要字典序最大的解,所以每层DFS要从大到小枚举,每次填的时候,要略过之前用过的数,并且填的时候一次性填两个位置,并且另一个填的位置需要之前没填过数。当枚举到第一个可行解的时候就可以返回了。代码如下:
public class Solution {
public int[] constructDistancedSequence(int n) {
// 答案的长度显然是2n - 1
int[] res = new int[2 * n - 1];
dfs(0, n, new boolean[n + 1], res);
return res;
}
private boolean dfs(int u, int n, boolean[] used, int[] res) {
// 枚举完了,没发现矛盾,则说明找到了一个解,返回true
if (u == 2 * n - 1) {
return true;
}
// 当前位置填了数了,说明这个数是之前某次枚举的时候填的,直接继续枚举下一个位置
if (res[u] != 0) {
return dfs(u + 1, n, used, res);
}
// 从大到小枚举当前位置应该填谁
for (int i = n; i > 1; i--) {
// 如果之前没用过i、u + i没越界、u + i的位置之前没填数,则枚举当前位置填i
if (!used[i] && u + i < 2 * n - 1 && res[u + i] == 0) {
res[u] = res[u + i] = i;
used[i] = true;
// 找到方案了则直接返回true
if (dfs(u + 1, n, used, res)) {
return true;
}
res[u] = res[u + i] = 0;
used[i] = false;
}
}
// 枚举一下1填在当前位置行不行
if (!used[1]) {
used[1] = true;
res[u] = 1;
if (dfs(u + 1, n, used, res)) {
return true;
}
used[1] = false;
res[u] = 0;
}
return false;
}
}
时间复杂度 O ( n ! ) O(n!) O(n!)(实际运行效果表面速度很快),空间 O ( n ) O(n) O(n)。