好,今天继续讲dfs
目录
先来道搜索题热热身
题目:降雨求水坑
思路:
思路很简单,就是搜索,先找到有水的位置,然后开始填水,把遍历过的水都给填上,然后再找下一个有水的位置,找着填着,填完再找。这样就能找到水坑数了。
要注意的是:水坑有8个方向相连,这里要小心一下
#include<cstdio>
using namespace std;
char a[101][101];
int ans,n,m;
void dfs(int x,int y){
a[x][y]='.'; //标记为已走,这样既能防止重复遍历这个点,也能减少一个分支数
int dx,dy;
for(int i=-1;i<=1;i++){//每个分支都有9-1个方向
for(int j=-1;j<=1;j++){
dx=x+i;
dy=y+j;
if(dx>=0&&dx<=n&&dy>=0&&dy<=m&&a[dx][dy]=='W'){//如果没有超过边界且为'W'的话就往那个点继续深入搜索
dfs(dx,dy);
}
}
}
return;//很多余,不写也没事
}
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++){
scanf("%s",a[i]);
}
for(int i=0;i<=n;i++){
for(int j=0;j<m;j++){
if(a[i][j]=='W'){//如果是W的话就直接开始遍历
dfs(i,j);//我们自动填水坑
ans++;//水潭加一处
}
}
}
printf("%d",ans);
return 0;
}
(上面是代码+注释)
好了,开始上正菜:
题目:八皇后问题
思路:
核心思想还是回溯,也就是试错嘛,遇错就返回到上个正确的状态。主要是怎么标记这个棋盘被皇后占领的状态呢?
首先啊:行列还是挺好标记的,r[x]表示第x行有皇后(对应1~n) , c[x]第x列有皇后(对应1~n)
那么斜着的线怎么办呢?别急!你先别急!我们不难发现:k为-1的线中的坐标有这样的规律:i-j相同(同直线上),k为1的线中的坐标有这样的规律:i+j一样(同直线上)。那么就简单了:
我们设置l1[ ]代表斜率为+1线,(共2n-1个线,同斜线各点i+j一样),l2[ ]代表斜率-1,(共2n-1个线,各点i-j一样)
但是l2[1-n->n-1]有负数,不妨直接优化一下变成l2[1-n+n->n-1+n]也就是l2[1->2n-1]最后就能dfs了,上代码!!!
#include <bits/stdc++.h>
using namespace std;
int r[100],c[100],l1[100],l2[100];//标记数组千万不能越界
int ans,n,a[9];
void print()
{
if(ans<=3)//输出前三组解法
{
for(int k=1;k<=n;k++) cout<<a[k]<<" ";
cout<<endl;
}
}
void dfs(int x){//x表示此时放置皇后数,默认和行数一一对应
if(x==n+1){//第n+1不用找,是结束条件
ans++;print();return ;
}
for(int i=1;i<=n;i++){ //回溯n次(每行有n个选择,放置到某一列嘛)就是找错了就认错(恢复)
if(!r[x]&&!c[i]&&!l1[i+x]&&!l2[x-i+n]){//对行,列,双斜线判断,都不能有放过皇后的
r[x]=1;c[i]=1;l1[i+x]=1;l2[x-i+n]=1;//放下皇后,标记四个状态
a[x]=i;dfs(x+1);//保存解法
r[x]=0;c[i]=0;l1[i+x]=0;l2[x-i+n]=0;//回溯(恢复状态)
}
}
}
int main(){
cin>>n;
dfs(1);//开始放第一个皇后
cout<<ans;
return 0;
}
好的,是不是有感觉了(感觉我们自己又行了)
上一道数独的题吧
题目:四维数独
题目我记得是这样的:我们要填4*4方格,每个方格都能填入1~4,但是要保证同行,同列,四个2*2的方格都不能有重复的数字(基本就是这个意思)
思路:
核心思想还是 ~ 回溯!唉,对!然后我们去想一下这个标记数组怎么整?
既然行,列都不能有重复数字,那么就设置a[x][i],b[y][j],c[b][k]表示第x行已有i数字,第y列已经有了j数字,第b块号已有k数字。
那么4个2*2方块内也不能出现同样的数字怎么办呢?
给一个方法哈:(row-1)/2*2+(col-1)/2+1,这样就分成了4个块号。(
你问我怎么来了?额……咱们会用就行了,哈哈哈哈)你真的不会了,那就if呗(弄他娘的4个if)
#include <bits/stdc++.h> //四阶数独(回溯) (关键是标记数组,如果当前数没出现过,那就尝试放呗)
using namespace std;
int ans;
int arr[17],a[5][5],b[5][5],c[5][5];//标记数组要够大,[4][4]是不够的
void dfs(int x){//当然可以写成二维的
if(x==17){
ans++;return ;
}
int row=(x-1)/4+1;//行号
int col=(x-1)%4+1;//列号(不要写c)
int block=(row-1)/2*2+(col-1)/2+1;//块号
for(int i=1;i<=4;i++){//每个位置都有4个数可以尝试
if(a[row][i]==0&&b[col][i]==0&&c[block][i]==0){//每行列块对应的i都未出现
arr[x]=i;a[row][i]=1;b[col][i]=1;c[block][i]=1;//保存,标记一下状态
dfs(x+1);a[row][i]=0;b[col][i]=0;c[block][i]=0;//回溯(恢复状态)
}
}
}
int main(){
dfs(1);//从第一个格子开始填充
cout<<ans;
return 0;
}
好了,以上就是今天的干货了。还是有点技术含量的,相信你能看明白的!