[Acwing提高]DFS连通性模型与搜索顺序
知识点
题目 | 扩展方式 | 类型 |
迷宫 | 看是否连通 | DFS连通性 |
红与黑 | Flood-Fill | DFS连通性 |
马走日 | 统计路径数 | DFS搜索顺序 |
单词接龙 | 字符串公共 | dfs搜索顺序 |
分成互质组 | 组合搜索 | dfs |
题目
迷宫
思路
起点不一定能走啊
代码
#include<bits/stdc++.h>
using namespace std;
const int N=110;
char g[N][N];
bool st[N][N];
int n,k;
int xa,ya,xb,yb;
int dx[]={-1,0,1,0},dy[]={0,1,0,-1};
bool dfs(int x,int y)
{
if(g[x][y]=='#')return 0;
if(x==xb&&y==yb)return 1;
st[x][y]=1;
for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<0||b<0||a>=n||b>=n)continue;
if(st[a][b])continue;
if(g[a][b]=='#')continue;
if(dfs(a,b))return 1;
}
return 0;
}
int main()
{
scanf("%d",&k);
while(k--)
{
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%s",&g[i]);
memset(st,0,sizeof st);
scanf("%d%d%d%d",&xa,&ya,&xb,&yb);
if(dfs(xa,ya))puts("YES");
else puts("NO");
}
return 0;
}
红与黑
思路
DFS实现Flood-Fill
代码
#include<bits/stdc++.h>
using namespace std;
const int N=25;
char g[N][N];
bool st[N][N];
int n,m;
int sx,sy;
int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
int dfs(int x,int y)
{
int cnt=1;
st[x][y]=1;
for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<0||b<0||a>=n||b>=m)continue;
if(st[a][b])continue;
if(g[a][b]=='#')continue;
cnt+=dfs(a,b);
}
return cnt;
}
int main()
{
while(cin>>m>>n&&n||m)
{
memset(st,0,sizeof st);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
cin>>g[i][j];
if(g[i][j]=='@')sx=i,sy=j;
}
cout<<dfs(sx,sy)<<endl;
}
return 0;
}
马走日
思路
统计路径数,要把整个图作为一个节点来考虑,描述状态为马的位置(x,y)以及当前是第几步了
代码
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int n,m;
bool st[N][N];
int ans;
int dx[]={1,1,-1,-1,2,2,-2,-2};
int dy[]={2,-2,2,-2,1,-1,1,-1};
void dfs(int x,int y,int cnt)
{
if(cnt==n*m)
{
ans++;
return;
}
for(int i=0;i<8;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<0||b<0||a>=n||b>=m)continue;
if(st[a][b])continue;
st[a][b]=1;
dfs(a,b,cnt+1);
st[a][b]=0;//回溯恢复
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
int x,y;
cin>>n>>m>>x>>y;
memset(st,0,sizeof st);
ans=0;
st[x][y]=1;//标记起点
dfs(x,y,1);
printf("%d\n",ans);
}
return 0;
}
yxc的
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int n,m;
bool st[N][N];
int ans;
int dx[]={1,1,-1,-1,2,2,-2,-2};
int dy[]={2,-2,2,-2,1,-1,1,-1};
void dfs(int x,int y,int cnt)
{
if(cnt==n*m)
{
ans++;
return;
}
st[x][y]=1;
for(int i=0;i<8;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<0||b<0||a>=n||b>=m)continue;
if(st[a][b])continue;
dfs(a,b,cnt+1);
}
st[x][y]=0;
}
int main()
{
int T;
cin>>T;
while(T--)
{
int x,y;
cin>>n>>m>>x>>y;
ans=0;
dfs(x,y,1);
printf("%d\n",ans);
}
return 0;
}
单词接龙
思路
先枚举符合首字母的,然后dfs搜,状态为拼接出来的字符串和最后用了哪个字符串
比较恶心的是处理公共部分长度
贪心一波肯定公共部分长度越短越好
我们可以直接预处理出公共长度存到二维数组里面(因为两个字符串拼接先后不同的话公共部分长度不一样的)
当我们要判断前串后缀与后串前缀是否相同的时候可以借助substr完成
对于答案更新:不用像其他dfs一样写边界直接更新即可
代码
#include<bits/stdc++.h>
using namespace std;
const int N=21;
int n;
string word[N];
int g[N][N];
int used[N];
int ans;
void dfs (string dragon,int last)//当前拼出来的以及上个用了哪个
{
ans=max((int)dragon.size(),ans);
used[last]++;
for(int i=0;i<n;i++)
if(g[last][i]&&used[i]<2)
dfs(dragon+word[i].substr(g[last][i]),i);
used[last]--;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)cin>>word[i];
char start;
cin>>start;
//预处理公共部分
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
string a=word[i],b=word[j];
for(int k=1;k<min(a.size(),b.size());k++)
if(a.substr(a.size()-k,k)==b.substr(0,k))
{
g[i][j]=k;
break;
}
}
for(int i=0;i<n;i++)
if(word[i][0]==start)
dfs(word[i],i);
cout<<ans<<endl;
}
分成互质组
思路
先枚举组数
如果分完了,那么更新最小值
决策:
1把某个数加到最后一组
2开个新组
如果可以选1那么就不选2
证:如果一个数放到下一组可以得到最优解,那么把这个数抠出来放原来组也可以得到最优解。因为抠出来以后新一组(后续搜索完整的时候)数之间还是互质的。
对于组内的数,我们要采用组合搜索的方式
代码
#include<bits/stdc++.h>
using namespace std;
const int N=15;
int n;
int p[N];
int group[N][N];
bool st[N];
int ans=N;
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
bool check(int group[],int gc,int i)//判组内互质
{
for(int j=0;j<gc;j++)
if(gcd(p[group[j]],p[i])>1)
return 0;
return 1;
}
//g当前做到哪一组,gc表示当前枚举到第几个数,tc表示已经处理多少数,st表示组内从哪个数开始枚举
void dfs(int g,int gc,int tc,int start)
{
if(g>=ans)return;
if(tc==n)ans=g;
bool flag=1;
for(int i=start;i<n;i++)//组合枚举
if(!st[i]&&check(group[g],gc,i))
{
st[i]=1;
group[g][gc]=i;
dfs(g,gc+1,tc+1,i+1);
st[i]=0;
flag=0;
}
if(flag)dfs(g+1,0,tc,0);
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)cin>>p[i];
dfs(1,0,0,0);
cout<<ans<<endl;
return 0;
}
总结
dfs比想象难很多,代码实现裂开了啊啊
dfs参数(状态的描述,当前操作描述)
可以开个二维数组预处理关系
明确答案边界,如果找不到可能就是搜不下去的时候直接更新答案