算法小记2 搜索之DFS
今日写完了洛谷上题单内的题,发现自己基本只会写板子,思维能力退化了蛮多,多练习吧。
题单:https://www.luogu.com.cn/training/985963#problems
1.图的遍历
DFS
难度:普及/提高-
洛谷地址:https://www.luogu.com.cn/problem/P3916
思路:
本题相当值得一做,很巧妙的思路。一开始试图正常遍历有向图,但终止条件比较难写,因为要判本来存的答案不能是要访问的节点本身。题解清一色的反向建图,从大节点开始往回推。dfs有两个参数,后一个是记录最大可达节点,由于是大节点回推,所以每个点都能遍历到最优解。同时需要注意用邻接表存二维数组,这样遍历比较省时间。
ac代码:
点击查看代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
const int N = 1e5 + 5;
int n,m;
vector<int> g[N];
int ans[N]; //ans存已访问过的答案+标记
void dfs(int x,int d){ //d为x最大可达节点
if(ans[x]) return;
ans[x]=d;
for(int i=0;i<g[x].size();i++) dfs(g[x][i],d);
}
signed main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y; cin>>x>>y;
g[y].push_back(x); //反向建边
}
for(int i=n;i>=1;i--) dfs(i,i);
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
return 0;
}
2.八皇后 Checker Challenge
其实是N皇后题
DFS
难度:普及/提高-
洛谷地址:https://www.luogu.com.cn/problem/P1219
思路:
很经典的一道n皇后,按照行dfs,然后一个一个点找可不可行,点的判定需要注意,用三个数组分别存列和两个主对角线,注意对角线的判定是平行。最后记得回溯即可。
ac代码:
点击查看代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
const int N = 30;
int n,cnt;
int col[N],d1[N],d2[N],ans[N];
void dfs(int x){ //行
if(x==n+1){
cnt++;
if(cnt<=3){
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
cout<<endl;
}
return;
}
for(int i=1;i<=n;i++){ //列
if(col[i]||d1[x-i+n]||d2[x+i]) continue;
// cout<<x<<" "<<i<<endl;
ans[x]=i;
col[i]=d1[x-i+n]=d2[x+i]=1;
dfs(x+1);
ans[x]=0;
col[i]=d1[x-i+n]=d2[x+i]=0;
}
}
signed main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
dfs(1);
cout<<cnt;
return 0;
}
3.Lake Counting S
DFS/BFS
难度:普及-
洛谷地址:https://www.luogu.com.cn/problem/P1596
思路:
本题没什么思维量,需要注意的就是主函数需要遍历每个点,以加到所有连通块。题解有个可以简化空间的方法就是不用vis数组,把遍历过的'W'改成'.'即可。
ac代码:
点击查看代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
const int dx[]={-1,1,0,0,1,1,-1,-1},dy[]={0,0,1,-1,1,-1,1,-1};
int n,m,cnt;
int vis[105][105];
char s[105][105];
void dfs(int x,int y){
if(s[x][y]=='.') return;
vis[x][y]=1;
for(int k=0;k<8;k++){
int nx=x+dx[k],ny=y+dy[k];
if(nx<1||nx>n||ny<1||ny>m||vis[nx][ny]) continue;
dfs(nx,ny);
}
}
signed main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>s[i]+1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s[i][j]=='W'&&!vis[i][j]){
dfs(i,j); cnt++;
}
}
}
cout<<cnt;
return 0;
}
4.滑雪
记忆化搜索(DP+DFS)
难度:普及/提高-
洛谷地址:https://www.luogu.com.cn/problem/P1434
思路:
非常好题。第一遍写的代码基本上就是暴搜,于是喜提超时。没想到什么优化解法,遂去看了解析,发现是记忆化搜索,即DP+DFS。一开始我以为先算下一步最长路会影响正确结果,毕竟很可能需要加上dfs的值,然后发现是先算最底端的最长路、在回溯过程中更新最长路的一个dp过程。
ac代码:
点击查看代码
// 记忆化搜索
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
const int N = 105;
const int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
int n,m,mn,cnt=1,ans;
int mp[N][N],f[N][N];
int dfs(int x,int y){
if(f[x][y]) return f[x][y]; //记忆化搜索
f[x][y]=1; //初始化
for(int k=0;k<4;k++){
int nx=x+dx[k],ny=y+dy[k];
if(nx<1||nx>n||ny<1||ny>m||mp[nx][ny]>=mp[x][y]) continue;
dfs(nx,ny); //先算下一步最长路
f[x][y]=max(f[x][y],f[nx][ny]+1); //回溯过程中才更新这一步最长路
}
return f[x][y];
}
signed main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>mp[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
ans=max(ans,dfs(i,j));
cout<<ans;
return 0;
}

浙公网安备 33010602011771号