算法提高课 第二章 搜索之DFS
作者:互联网
一、DFS之连通性模型
1112. 迷宫
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
int T,n;
char g[N][N];
int sx,sy,ex,ey;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
bool st[N][N],f;
void dfs(int x,int y)
{
if(x == ex && y == ey)
{
f = 1;
return;
}
st[x][y] = 1;
for(int i = 0;i<4;i++)
{
int nx = x + dx[i],ny = y + dy[i];
if(nx<0||nx>=n||ny<0||ny>=n||g[nx][ny] == '#'||st[nx][ny]) continue;
dfs(nx,ny);
}
}
int main()
{
cin>>T;
while(T--)
{
memset(st,0,sizeof st);
cin>>n;
for(int i = 0;i<n;i++)
{
for(int j = 0;j<n;j++)
{
cin>>g[i][j];
}
}
cin>>sx>>sy>>ex>>ey;
if(g[sx][sy] == '#'||g[ex][ey] == '#')
{
puts("NO");
continue;
}
f = false;
dfs(sx,sy);
if(f) puts("YES");
else puts("NO");
}
return 0;
}
1113. 红与黑
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 25;
char g[N][N];
int n,m,T;
bool st[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int ans;
void dfs(int x,int y)
{
st[x][y] = 1;
++ans;
for(int i = 0;i<4;i++)
{
int nx = x + dx[i],ny = y + dy[i];
if(nx<1||nx>n||ny<1||ny>m||st[nx][ny]||g[nx][ny] == '#') continue;
dfs(nx,ny);
}
return;
}
int main()
{
while(cin>>m>>n,n||m)
{
ans = 0;
memset(st, 0, sizeof st);
int sx,sy;
for (int i = 1; i <= n; i ++ )
{
for(int j = 1;j<=m;j++)
{
cin>>g[i][j];
if(g[i][j] == '@')
{
sx = i,sy = j;
}
}
}
dfs(sx,sy);
cout<<ans<<endl;
}
return 0;
}
1118. 分成互质组
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 15;
int group[N][N];//第i组第j个元素
int n,a[N];
int ans = 0x3f3f3f3f;
bool st[N];
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
bool check(int g,int gc,int x) //判断x是否与g组所有元素互质
{
for(int i = 0;i<gc;i++)
{
if(gcd(group[g][i],x)>1) return false;
}
return true;
}
void dfs(int g,int gc,int u,int start) //g:第几组,gc:组里有多少个数 u:当前搜到了第几个元素 start:搜索起始点
{
if(g >= ans) return;//当前组数超过了答案,一定不可行,剪枝
if(u == n) //搜完了所有元素,求出组数最小值
{
ans = min(ans,g);
return;
}
bool f = false; //当前组是否添加进新元素
for(int i = start;i<n;i++)
{
if(!st[i] && check(g,gc,a[i]))//没有添加过且与组里元素都互质
{
group[g][gc] = a[i];//添加进组
st[i] = true;
dfs(g,gc+1,u+1,start+1);//搜索下一个元素
f = true;//修改已添加
st[i] = false;//恢复现场
}
}
if(!f) //当前组无法添加元素,新开一个组,从头开始搜
{
dfs(g+1,0,u,0);
}
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
{
scanf("%d", &a[i]);
}
dfs(1,0,0,0);
cout<<ans<<endl;
return 0;
}
二、DFS之搜索顺序
1116. 马走日
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 10;
int T,n,m,x,y;
LL ans;
int dx[] = {-1,-2,-2,-1,1,2,2,1},dy[] = {-2,-1,1,2,2,1,-1,-2};
bool st[N][N];
void dfs(int x,int y,int cnt)
{
if(cnt == n*m) //注意:搜到第n*m个点时就可以返回了
{
++ans;
return;
}
st[x][y] = 1;
for(int i = 0;i<8;i++)
{
int nx = x + dx[i],ny = y + dy[i];
if(nx<0||nx>=n||ny<0||ny>=m||st[nx][ny]) continue;
dfs(nx,ny,cnt+1);
}
st[x][y] = 0;//注意:恢复现场写在外面
}
int main()
{
cin>>T;
while(T--)
{
ans = 0;
memset(st,0,sizeof st);
cin>>n>>m>>x>>y;
dfs(x,y,1);
cout<<ans<<endl;
}
return 0;
}
1117. 单词接龙
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
const int N = 25;
string word[N];
int n;
int ans;
int st[N];
void dfs(string cur,int u) //理解dfs函数形参结构,cur为当前拼接字符串,u为最后一次加入的字符串
{
//cout<<cur<<endl;
ans = max(ans,(int)cur.size());
++st[u];//不能写进循环体内
for(int i = 0;i<n;i++) //枚举所有字符串
{
if(st[i] == 2) continue; //注意次数限制
for(int j = 1;j<min(cur.size(),word[i].size());j++)
{
if(cur.substr(cur.size()-j) == word[i].substr(0,j)) //能拼接则进行拼接
{
dfs(cur + word[i].substr(j),i);
}
}
}
--st[u];//不能写进循环体内
}
int main()
{
cin>>n;
for(int i = 0;i<n;i++)
{
cin>>word[i];
}
char c;
cin>>c;
for(int i = 0;i<n;i++)
{
if(word[i][0] == c)
{
dfs(word[i],i);
}
}
cout<<ans<<endl;
return 0;
}
1118. 分成互质组
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 15;
int group[N][N];//第i组第j个元素
int n,a[N];
int ans = 0x3f3f3f3f;
bool st[N];
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
bool check(int g,int gc,int x) //判断x是否与g组所有元素互质
{
for(int i = 0;i<gc;i++)
{
if(gcd(group[g][i],x)>1) return false;
}
return true;
}
void dfs(int g,int gc,int u,int start) //g:第几组,gc:组里有多少个数 u:当前搜到了第几个元素 start:搜索起始点
{
if(g >= ans) return;//当前组数超过了答案,一定不可行,剪枝
if(u == n) //搜完了所有元素,求出组数最小值
{
ans = min(ans,g);
return;
}
bool f = false; //当前组是否添加进新元素
for(int i = start;i<n;i++)
{
if(!st[i] && check(g,gc,a[i]))//没有添加过且与组里元素都互质
{
group[g][gc] = a[i];//添加进组
st[i] = true;
dfs(g,gc+1,u+1,start+1);//搜索下一个元素
f = true;//修改已添加
st[i] = false;//恢复现场
}
}
if(!f) //当前组无法添加元素,新开一个组,从头开始搜
{
dfs(g+1,0,u,0);
}
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
{
scanf("%d", &a[i]);
}
dfs(1,0,0,0);
cout<<ans<<endl;
return 0;
}
三、DFS之剪枝
165. 小猫爬山
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20;
int sum[N],c[N],n,w;
int ans;
void dfs(int cnt,int u) //cnt表示当前用了几辆车,u表示枚举到第几只猫
{
if(cnt >= ans) return;
if(u == n+1)
{
ans = min(ans,cnt);
return;
}
for(int i = 0;i<cnt;i++) //枚举所有车,能放第u只猫则放
{
if(sum[i] + c[u] > w) continue; //可行性剪枝
sum[i] += c[u]; //加入第i辆车
dfs(cnt,u+1); //为下一只猫找车
sum[i] -= c[u]; //注意:恢复现场
}
sum[cnt] = c[u]; //新开一辆车,放第u只猫
dfs(cnt+1,u+1); //枚举下一只猫
sum[cnt] = 0; //恢复现场
return;
}
bool cmp(int a,int b)
{
return a>b; //必须是>号
}
int main()
{
cin>>n>>w;
for(int i = 1;i<=n;i++)
{
cin>>c[i];
}
ans = n;
sort(c+1,c+n+1,cmp); //降序排序,减少搜索层数
dfs(0,1);
cout<<ans<<endl;
return 0;
}
数独
优化搜索顺序:优先搜索分支较小的,即格子中可能的数较少的
可行性剪枝:所在行、所在列以及所在九宫格中没有重复数字
位运算优化:用九位二进制数表示1~9是否可用,行、列及九宫格三个值进行与运算,lowbit取出所有的1,减少循环次数
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 9,M = 1<<N;
int ones[M];//预处理:一个状态里面有多少个1
int map[M]; //预处理:lowbit返回的是2的次幂,因此需要求出2的几次幂 map(x) = log2(x)
int row[N],col[N],cell[3][3]; //每一行、每一列、每个九宫格能放的数字的二进制表示
char str[100];
int lowbit(int x)
{
return x & -x;
}
int get(int x,int y) //返回当前位置能填的数字方案的二进制表示
{
return row[x] & col[y] & cell[x/3][y/3];
}
void init() //初始化:将所有行、列、九宫格的状态设置为1~9可填
{
for(int i = 0;i<N;i++)
{
col[i] = row[i] = (1<<N) - 1;
}
for(int i = 0;i<3;i++)
{
for(int j = 0;j<3;j++)
{
cell[i][j] = (1<<N) - 1;
}
}
}
void draw(int x,int y,int t,bool f) //在位置(x,y)上填数字t(f = 1) or 删去t(f = 0)
{
//对字符串进行实际的填入或删除操作
if(f) str[x*N + y] = t + '1';
else str[x*N + y] = '.';
//更新对应行、列、九宫格的状态
int v = 1<<t;
if(f)
{
row[x] -= v;
col[y] -= v;
cell[x/3][y/3] -= v;
}
else
{
row[x] += v;
col[y] += v;
cell[x/3][y/3] += v;
}
}
bool dfs(int cnt)
{
if(cnt == 0) return true;
//优化操作:找到可填数字最少的格子(分支数最少)
int minv = 10;
int minx,miny;
int k = 0;
for(int x = 0;x<N;x++)
{
for(int y = 0;y<N;y++,k++)
{
if(str[k] == '.') //为空
{
int state = get(x,y); //得到方案的二进制表示
if(ones[state] < minv) //方案中几个1表示有几个数字可填
{
minv = ones[state];
minx = x,miny = y;
}
}
}
}
//从分支最少的方案开始搜
int state = get(minx,miny);
for(int i = state;i;i -= lowbit(i)) //枚举状态中所有1
{
int t = map[lowbit(i)]; //取出1所表示的能放的数字
draw(minx,miny,t,1); //放入该数字
if(dfs(cnt-1)) return true; //搜索下一个位置,若成功放置,返回true
draw(minx,miny,t,0);//恢复现场
}
return false; //找不到任何方案,返回false
}
int main()
{
for(int i = 0;i<N;i++) //预处理:lowbit返回的是2的次幂,因此需要求出2的几次幂 map(x) = log2(x)
{
map[1<<i] = i;
}
for(int i = 0;i<M;i++) //预处理:一个状态里面有多少个1
{
for(int j = 0;j<N;j++)
{
ones[i] += i >> j & 1;
}
}
while(cin>>str,str[0] != 'e')
{
init();//初始化
int cnt = 0; //空格子的个数
int k = 0;//枚举所有格子
for(int x = 0;x<N;x++)//x,y表示位置
{
for(int y = 0;y<N;y++,k++)
{
if(str[k] != '.')
{
int t = str[k] - '1'; //注意:减去'1'
draw(x,y,t,1); //根据给定格子的数更新状态
}
else ++cnt;
}
}
dfs(cnt);
puts(str);
}
return 0;
}
木棒
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 70;
int sum,n,Max;
int a[N];
int len;
bool st[N]; //第i根小棒是否被使用
bool dfs(int cnt,int start,int cur) //cnt:当前组装的长度为len的大棒个数,start为枚举起点
{
if(cnt*len == sum) return true; //搜索到结果
if(cur == len) return dfs(cnt+1,0,0);//当前大棒已组成,组装下一根
//剪枝1:排除等效冗余,从start开始枚举
for(int i = start;i<n;i++)
{
if(st[i]) continue;//使用过的不用
if(cur + a[i] > len) continue; //剪枝2:可行性剪枝,超长度的不用
st[i] = 1; //标记已使用
if(dfs(cnt,i+1,cur + a[i])) return true; //若能组装
//若不能组装
st[i] = 0;//恢复现场
if(cur == 0) return false; //剪枝3:可行性剪枝,当前第一根小棒都加入失败,则一定失败
if(cur + a[i] == len) return false; //剪枝4:可行性剪枝,当前最后一根不同长度的小棒加入失败,则一定失败
int j;
for(j = i;j<n && a[j] == a[i];j++);//剪枝5:可行性剪枝,当前长度小棒加入失败,则后面相同的也一定失败
i = j - 1;
}
return false;
}
int main()
{
while(cin>>n,n)
{
memset(st, 0, sizeof st);
sum = 0,Max = 0;
for(int i = 0;i<n;i++)
{
cin>>a[i];
Max = max(Max,a[i]);//每个相同小棒的长度必须大于给定小棒的最大值
sum += a[i];
}
sort(a,a+n);//优化搜索顺序:降序排序,减少搜索分支
reverse(a,a+n);
for(len = Max;;len++)//优化搜索顺序:从最大值开始枚举
{
if(sum % len == 0 && dfs(0,0,0))
{
cout<<len<<endl;
break;
}
}
}
return 0;
}
168. 生日蛋糕
标签:第二章,return,int,DFS,st,算法,dfs,ans,include 来源: https://www.cnblogs.com/zjq182/p/16383611.html