C++专题五学习(深度算法)
深度优先探索(DFS)
适用全排列问题等需要列举出全部情况的 (配合递归
大致模版:
void dfs(...){if(递归结束条件){...; //计入答案return;}for(int i=0;i<n;i++){ //有的有循环有的没有if(需要满足的条件){...;dfs(...+1);撤销; //比如前面++,这里就--}}
}
int main(){...;dfs(开始的位置);...;
}
例1:洛谷p1219八皇后
每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
Input
一行一个正整数 n,表示棋盘是 n×n大小的。
Output
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
#include<bits/stdc++.h>
using namespace std;
const int N=14;
int y[N],d1[2*N],d2[2*N];
vector<int> v;
vector<vector<int>> ans;int n;
void dfs(int i){ //一行一行放if(i>n){ans.push_back(v);return;}for(int j=1;j<=n;j++){if(y[j]==0&&d1[i-j+n]==0&&d2[i+j]==0){y[j]++;d1[i-j+n]++;d2[i+j]++;v.push_back(j);dfs(i+1);//撤销v.pop_back();y[j]--;d1[i-j+n]--;d2[i+j]--;}}
}
int main(){cin>>n;dfs(1);sort(ans.begin(),ans.end());for(int i=0;i<3;i++){ //输出for(const auto& el: ans[i]){cout<<el<<" ";}cout<<endl;}cout<<ans.size()<<endl;return 0;
}
例2:洛谷p2404自然数拆分问题
sample
Input
7
Output
1+1+1+1+1+1+1
1+1+1+1+1+2
1+1+1+1+3
1+1+1+2+2
1+1+1+4
1+1+2+3
1+1+5
1+2+2+2
1+2+4
1+3+3
1+6
2+2+3
2+5
3+4
#include<bits/stdc++.h>
using namespace std;
int n,t=0,a[10000];
void dfs(int cur,int sum){ //从cur开始拆,防止重复if(sum==0){cout<<a[0];for(int i=1;i<t;i++){cout<<"+"<<a[i];}cout<<endl;return ;}if(cur>sum||cur==n)return ;a[t++]=cur;dfs(cur,sum-cur);t--;dfs(cur+1,sum);
}
int main(){cin>>n;dfs(1,n);return 0;
}
广度优先探索(BFS)
适用于需要求最值的,或需要求板块之类的(配合队列
大致模版:
...
//四维方向
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
//或者八维
int dx[]={0,0,1,-1,1,1,-1,-1};
int dy[]={1,-1,0,0,-1,1,-1,1};
bool vis[N][N]; //标记(害怕重复选到的需要标记,如果没有这个问题就不需要)
void bfs(int sx,int sy){...;queue<pair<int,int>> q;vis[sx][sy]=true;q.push({sx,sy});while(!q.empty()){int x=q.front().first;int y=q.front().second;q.pop();for(int i=0;i<4(或者是8);i++){int nx=x+dx[i];int ny=y+dy[i];if(nx和ny没有超出边界且符合条件){...; // 计入答案q.push({nx,ny});}}}
}
int main(){...;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(!vis[i][j]){bfs(i,j);}}} //或者直接从头开始探索...;
}
例1:洛谷p1162填涂颜色
被1包围的0要被填上2。
sample
Input
6
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
Output
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1
#include<bits/stdc++.h>
using namespace std;
int n;
int a[31][31],b[31][31],t;
struct xy{int x,y;
};
queue<xy> q;
int fx[4]={-1,1,0,0};
int fy[4]={0,0,-1,1};
int main(){cin>>n;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){cin>>t;if(t==1) a[i][j]=1;}}b[0][0]=1;q.push(xy{0,0});while(!q.empty()){xy p=q.front();q.pop();for(int i=0;i<4;i++){int x=p.x+fx[i];int y=p.y+fy[i];if(x<0||x>n+1||y<0||y>n+1) continue;if(a[x][y]==0&&b[x][y]==0){b[x][y]=1;q.push(xy{x,y});}}}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(a[i][j]==0&&b[i][j]==0)cout<<"2"<<" "; // b[i][j]是标签,没被打上标签的0可以变为2else if(a[i][j]==1)cout<<"1"<<" ";else cout<<"0"<<" ";}cout<<endl;} return 0;
}
例2:洛谷p3456山峰山谷
比周围的一片高的那块是山峰,比周围一片矮的是山谷,整片一样高的既是山峰又是山谷,输出山峰,山谷数量
#include<bits/stdc++.h>
using namespace std;
const int N=1000+5;int dx[]={0,0,1,-1,1,1,-1,-1};
int dy[]={1,-1,0,0,-1,1,-1,1};
int n,a[N][N];int cntr,cntv;
bool vis[N][N];
void bfs(int sx,int sy){bool is_r=true;bool is_v=true;queue<pair<int,int>> q;vis[sx][sy]=true;q.push({sx,sy});while(!q.empty()){int x=q.front().first;int y=q.front().second;q.pop();for(int i=0;i<8;i++){int nx=x+dx[i];int ny=y+dy[i];if(nx>=1&&nx<=n&&ny>=1&&ny<=n){if(a[nx][ny]==a[x][y]&&!vis[nx][ny]){vis[nx][ny]=true;q.push({nx,ny});}if(a[nx][ny]<a[x][y]){is_v=false;}if(a[nx][ny]>a[x][y]){is_r=false;} }}}if(is_r)cntr++;if(is_v)cntv++;
}int main(){cin>>n;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){cin>>a[i][j];}}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(!vis[i][j]){bfs(i,j);}}}cout<<cntr<<" "<<cntv<<endl;return 0;
}