五月集训(第15天)—深度优先搜索(DFS)
深度优先搜索(Depth First Search, DFS)
1. 565. 数组嵌套
思路:
一开始以每个点为起点暴力深搜,结果超时了。发现染色法是真不错,每次深搜染成不一样的颜色,这样染过色的点就不用再当成起点了,因为它必然是之前答案的后缀,集合元素不是最大的。而且利用染色法深搜只需要将hash
初始化一次即可。
class Solution {
int hash[200010];
int cnt = 0;
public:
void dfs(int index, vector<int> &nums, int color) {
if (hash[index] != -1) return ;
hash[index] = color;
cnt++;
dfs(nums[index], nums, color);
}
int arrayNesting(vector<int>& nums) {
int color = 0, maxn = 0;
int n = nums.size();
memset(hash, -1, sizeof(hash));
for (int i = 0; i < n; i++) {
if (hash[i] == -1) {
cnt = 0;
dfs(nums[i], nums, color++);
maxn = max(maxn, cnt);
}
}
return maxn;
}
};
2. 401. 二进制手表
思路:
把表示时间的LED和表示分钟的LED放一起看,认为有10个LED,求从中取出turnedOn
个的全排列,按照全排列的对应LED求出表示的时间即可。
class Solution {
int hours[10] = {1, 2, 4, 8, 0, 0, 0, 0, 0, 0};
int minutes[10] = {0, 0, 0, 0, 1, 2, 4, 8, 16, 32};
bool vis[11];
public:
void dfs(vector<string> &ret, int turnedOn, int hour, int minute, int index) {
if (hour > 11 || minute > 59) return ;
if (turnedOn == 0) {
string ans;
ans = to_string(hour) + ":" + (minute < 10 ? ("0" + to_string(minute)) : to_string(minute));
ret.push_back(ans);
return ;
}
for (int i = index; i < 10; i++) {
if (!vis[i]) {
vis[i] = true;
dfs(ret, turnedOn - 1, hour + hours[i], minute + minutes[i], i);
vis[i] = false;
}
}
}
vector<string> readBinaryWatch(int turnedOn) {
vector<string> ret;
memset(vis, 0, sizeof(vis));
dfs(ret, turnedOn, 0, 0, 0);
return ret;
}
};
3. 1079. 活字印刷
思路:
求字母的组合
- 首先将字母与数字1~7建立映射关系。
- 求出数字排列的所有可能(包括各种前缀)。
- 将当前的排列与一个整数建立映射关系,利用hash判断当前排列是否出现过,未出现过则结果
cnt++
class Solution {
int vis[7777777+10]; // 7的全排列转化为整数表示
bool vis_map[10];
int cnt = 0;
public:
void dfs(int mmap[], int index, int len, int ans) {
if (index != 0) cnt++;
for (int i = 1; i <= len; i++) {
if (!vis_map[i]) {
vis_map[i] = 1;
if (-1 == vis[ ans * 10 + mmap[i] ]) {
vis[ ans * 10 + mmap[i] ] = 1;
dfs(mmap, i, len, ans * 10 + mmap[i]);
}
vis_map[i] = 0;
}
}
}
int numTilePossibilities(string tiles) {
int len = tiles.length();
// 将tiles压缩为0~6之间的数字,方便作全排列
int order = 0;
int nums[27], mmap[10];
memset(nums, -1, sizeof(nums));
for (int i = 0; i < len; i++) {
if (-1 == nums[tiles[i] - 'A']) {
nums[tiles[i] - 'A'] = ++order;
}
mmap[i + 1] = nums[tiles[i] - 'A'];
}
// 求全排列
memset(vis, -1, sizeof(vis));
memset(vis_map, 0, sizeof(vis_map));
dfs(mmap, 0, len, 0);
return cnt;
}
};
4. 1219. 黄金矿工
思路:
每次选取一个新的起点,从起点出发向四周寻找非零的矿,找到则财富累积,找不到且无路可走时返回。利用深度优先搜索 + 回溯实现。
class Solution {
bool vis[16][16];
int dir[4][2] = {{0, -1}, {1, 0}, {0, 1}, {-1, 0}};
int maxn = 0;
int n, m;
public:
int check(int x, int y) {
if (x < 0 || x >= n || y < 0 || y >= m) return 0;
if (vis[x][y]) return 0;
return 1;
}
void dfs(vector<vector<int>> &grid, int cnt, int last_x, int last_y) {
maxn = max(maxn, cnt);
int x, y;
for (int i = 0; i < 4; i++) {
x = last_x + dir[i][0];
y = last_y + dir[i][1];
if (check(x, y) && grid[x][y] > 0) {
vis[x][y] = 1;
dfs(grid, cnt + grid[x][y], x, y);
vis[x][y] = 0;
}
}
}
int getMaximumGold(vector<vector<int>>& grid) {
n = grid.size();
m =grid[0].size();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] > 0) {
vis[i][j] = 1;
dfs(grid, grid[i][j], i, j);
vis[i][j] = 0;
}
}
}
return maxn;
}
};
东方欲晓,莫道君行早。