dfs:枚举所有的解决方案。
实现:每次判断这步是否可行,不可行就返回;再判断是否为最后一步,是则返回或者输出;否则就是继续搜索。记得搜索结束要回溯。
bfs:找到最快的一条路径。
实现:将合法的下一步,都压入队列中,每次判断队列是否为空,不空则取出队首,继续判断。记录路径的话,就保存它的前驱点,等到终止时,再输出即可。
八皇后******
每行每列,所有对角线上 都只有一个皇后。
分析:我们逐行进行搜索。每搜索到一个位置,就将该列,和两个对角线 标记为已访问。
vis1[j]=vis2[i+j]=vis3[i-j+n]=1;
回溯时,清零即可。
代码:
#include
using namespace std;
const int maxn=105;
int vis1[maxn],vis2[maxn],vis3[maxn];
int n,ans[maxn],tot=0;//要求计算总方案数,并输出前三组(存在ans里)
void dfs(int i)
{
if(i>n)
{
if(tot<3)
{
for(int j=1;j<=n;j++)
cout<>n;
memset(vis1,0,sizeof(vis1));
memset(vis2,0,sizeof(vis2));
memset(vis3,0,sizeof(vis3));
dfs(1);
cout<
迷宫总方案数*********
有路和墙,起点终点,求方案数。搜索跑图,一般套路。
代码:
#include
using namespace std;
const int maxn=6;
int mp[maxn][maxn];
bool vis[maxn][maxn];
int dx[4]={0,0,1,-1};
int dy[4]={-1,1,0,0};
int total,fx,fy,sx,sy,T,n,m,l,r;//total计数器
void dfs(int x,int y)
{
if(x==fx&&y==fy)//到达终点
{
total++;
return;//返回,继续搜索;
}
else
{
for(int i=0;i<4;i++)//左,右,下,上四个方向;
{
int xx=x+dx[i],yy=y+dy[i];//下一步坐标
if(vis[xx][yy]==0&&mp[xx][yy])//判断没有走过 且 可以走
{
vis[x][y]=1;//标记已走过
dfs(xx,yy);
vis[x][y]=0;//回溯清零
}
}
}
}
int main()
{
cin>>n>>m>>T;//题目中的 n,m长度宽度,T障碍个数
for(int i=1;i<=n;i++)//初始化全图均可走
for(int j=1;j<=m;j++)
mp[i][j]=1;
cin>>sx>>sy;//起始x,y
cin>>fx>>fy;//结束x,y
for(int u=1;u<=T;u++)
{
cin>>l>>r;//l,r是障碍坐标;
mp[l][r]=0;
}
dfs(sx,sy);
cout<
单词方阵********
找出连成块的“yizhong”的单词方阵,其他单词用*替换。八个方向。
分析:先找到y 再找到i,确定了某个方向,才确认此方向可以考虑搜索
单词可以重叠,那么我们就需要记录一下单词的位置,成功结束后,再将标记一起置1
搜索时,试探下一个位置,和跑图类似
代码:
#include
using namespace std;
const int maxn=105;
char mp[maxn][maxn];
char str[]={"yizhong"};
int vis[maxn][maxn],ansx[maxn],ansy[maxn];//用于记录路径
int to[][2]={-1,0,0,-1,1,0,0,1,1,1,1,-1,-1,1,-1,-1};//左下右上,右上右下,左上,左下
int n;
void dfs(int x,int y,int k,int step)//k 所走的方向,step 所匹配到的字符串下标
{
if(step==7)
{
for(int i=0;i<7;i++)
vis[ansx[i]][ansy[i]]=1;
}
else
{
int xx=x+to[k][0];
int yy=y+to[k][1];
if(step==6 || mp[xx][yy]==str[step+1])
{
ansx[step]=x,ansy[step]=y;
dfs(xx,yy,k,step+1);
}
}
}
int main()
{
cin>>n;
for(int i=0;i>mp[i];
memset(vis,0,sizeof(vis));
///首先要在矩阵中找到 y i相连的块,后来再按照这个方向搜索才行
for(int i=0;i
填涂颜色**
整个图形只有0、1,把所有被1包围的0 变成2(只有一个1的环),并输出整张图。
分析:让我们找到被1包围的0的连通块,很难。
但是,我们会发现,因为只有一个环,那么环的外面的那些0可以找出来。
怎么找?从边界开始bfs,只要是0都标记了。最后那些没有被标记的0,也就是被1包围的0了。
#include
#include
using namespace std;
const int maxn=40;
int n;
int vis[maxn][maxn],mp[maxn][maxn];
int to[][2]={1,0,0,1,-1,0,0,-1};
struct node
{
int x,y;
};
void bfs(int sx,int sy)
{
queueq;
q.push({sx,sy});
vis[sx][sy]=1;
mp[sx][sy]=-1;
while(!q.empty())
{
node now=q.front();q.pop();
for(int i=0;i<4;i++)
{
int xx=now.x+to[i][0],yy=now.y+to[i][1];
if(!vis[xx][yy]&&mp[xx][yy]==0&&xx>=1&&xx<=n&&yy>=1&&yy<=n)
{
vis[xx][yy]=1;
mp[xx][yy]=-1;
q.push({xx,yy});
}
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
cin>>mp[i][j];
if(mp[i][j]) vis[i][j]=1;
}
for(int i=1;i<=n;i++)
{
if(!vis[i][1]&&mp[i][1]==0)
bfs(i,1);
if(!vis[i][n]&&mp[i][n]==0)
bfs(i,n);
if(!vis[1][i]&&mp[1][i]==0)
bfs(1,i);
if(!vis[n][i]&&mp[n][i]==0)
bfs(n,i);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(mp[i][j]==-1) cout<<"0";
else if(mp[i][j]==1) cout<<"1";
else if(mp[i][j]==0) cout<<"2";
if(j
马的遍历*
马走日字,八个方向。
给出图的大小,和马的起点,求该点到涂上所有点的最短步数。
bfs(sx,sy);即可
#include
#include
using namespace std;
const int maxn=404;
int n,m,sx,sy;
int vis[maxn][maxn],ans[maxn][maxn];
int to[][2]={1,2,2,1,-1,2,2,-1,1,-2,-2,1,-1,-2,-2,-1};
struct node
{
int x,y,step;
}tt;
void bfs()
{
vis[sx][sy]=1;
ans[sx][sy]=0;
queueq;
tt.x=sx,tt.y=sy,tt.step=0;
q.push(tt);
while(!q.empty())
{
node now=q.front();q.pop();
for(int i=0;i<8;i++)
{
int xx=now.x+to[i][0],yy=now.y+to[i][1];
if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&!vis[xx][yy])
{
vis[xx][yy]=1;
tt.x=xx,tt.y=yy,tt.step=now.step+1;
q.push(tt);
ans[xx][yy]=tt.step;
}
}
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&sx,&sy);
memset(vis,0,sizeof(vis));
memset(ans,-1,sizeof(ans));
bfs();
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
printf("%-5d",ans[i][j]);
}
puts("");
}
return 0;
}
字串变换****
有两个字串A,B及一组字串变换的规则(至多6个规则)
若在10步(包含10步)以内能将A变换为B,则输出最少的变换步数
分析:对于每个当前待修改串,我们都有 其长度的修改方式。每个位置i都有可能用某种规则j替换。所以,这是个复制的过程,也是不断重复的,我们就写个函数来实现。(1)
bfs的知识就很普通,把合法的新串存起来就是,直到找到目标串为止(2)
有一个坑点,如果你不记录一下某个串是否已经搜索过了,而重复去搜,会tle。(3)
实现:(1)用string的方法,且首先判断一下合法性,再拼接string.substr(index_begin,index_end)返回。
(3)用map
代码:
#include
using namespace std;
const int maxn=20;
string a,b;
string ta[maxn],tb[maxn];
mapma;
int n=0,minn=0;
struct node
{
string str;
int step;
};
string checkk(const string &str,int i,int j)
{
string ans="";
if(i+ta[j].length()>str.length()) return ans;
for(int k=0;kq;
node x;
x.str=a;x.step=0;
q.push(x);
while(!q.empty())
{
node now=q.front();
q.pop();
if(ma.count(now.str)==1) continue;//剪枝!!
ma[now.str]=1;
if(now.str==b)
{
minn=now.step;
break;
}
for(int i=0;i>a>>b;
while(cin>>ta[n]>>tb[n]) n++;
bfs();
if(minn==0||minn>10) cout<<"NO ANSWER!"<
数字三角形**
给出N,sum,求1-N的一个排列,使得每层为上一层相邻两个数相加,找出层数为N的值为sum的排列。
思路:(1)每层为上一层两个数相加:每个数字有重复加,考虑一下每个数字重复次数
分析得 规律即为杨辉三角。
(2)这么找出这样的排列呢?a).next_permutation生成排列。b).dfs
(3)注意剪枝,当前和已经超过sum 剪掉就好
#include
using namespace std;
int yh[15][15],n,sum;
void yanghui()
{
memset(yh,0,sizeof(yh));
yh[1][1]=1;
for(int i=2;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
yh[i][j]=yh[i-1][j-1]+yh[i-1][j];
}
}
}
int vis[15];//每个数字i是否已经用到了
int res[15],f=0;//保存最后的那个排列序列
void dfs(int step,int ans)
{
if(f||ans>sum) return;
if(step==n+1 && ans==sum)
{
for(int i=1;i<=n;i++)
cout<>n>>sum;
yanghui();
dfs(1,0);
}
开了氧气优化的a方法代码
#include
#include
#include
using namespace std;
int res[15];
int yh[15][15];
void yanghui(int n)
{
memset(yh,0,sizeof(yh));
yh[1][1]=1;
for(int i=2;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
yh[i][j]=yh[i-1][j-1]+yh[i-1][j];
}
}
}
int main()
{
int n,sum;
cin>>n>>sum;
yanghui(n);
for(int i=1; i<=n; i++) res[i]=i;
do {
int tsum=0;
for(int i=1;i<=n;i++)
tsum+=res[i]*yh[n][i];
if(tsum==sum)
{
for(int i=1;i<=n;i++)
cout<
滑雪****
给出一张图,找出一条最长路线,路线上的值严格单调减。
分析:
(1)我们需要对于每个位置都要进行搜索,找出最短的一条。但是可能找到某个位置是,之前已经找出了,从这个位置出发的最长的一条路线,再重复搜索就是浪费时间,所有我们可以把这个位置开始的最大值记录下来。下次遇到了,直接返回就行。——记忆化搜索
dis[x][y]=d;
int dfs(int x,int y){
if(dis[x][y])return dis[x][y];
…
}
(2)dfs找出最长的那一条,每走一步就+1,我们可以利用带返回值类型的dfs进行优化,也不需要再用一个专门的标记表示某点是否走过了。 d=max(dfs(xx,yy)+1,d);
#include
using namespace std;
const int maxn=105;
int n,m,mp[maxn][maxn],maxx;
int dis[maxn][maxn];
int to[][2]={1,0,0,1,-1,0,0,-1};
int dfs(int x,int y)
{
if(dis[x][y])return dis[x][y];
int d=1;
for(int i=0;i<4;i++)
{
int xx=x+to[i][0],yy=y+to[i][1];
if(xx<0||xx>=n||yy<0||y>=n||mp[xx][yy]>=mp[x][y])continue;
d=max(dfs(xx,yy)+1,d);
}
dis[x][y]=d;
return d;
}
int main()
{
cin>>n>>m;
for(int i=0;i>mp[i][j];
memset(dis,0,sizeof(dis));
for(int i=0;imaxx)
maxx=d;
}
cout<
吃奶酪*****
给出n组坐标,问从(0,0)出发走完所有坐标的最短距离。两点间距离,用距离公式计算。
分析:题意明显要用dfs,当前方案走完所有点step==n的话,就比较一下,更新最小值
当前的最短距离,存在数组里不合适,用返回值也不能保证是最短的,所以选择存在dfs形参里。同时这个值,可以提供来剪枝,如果当前搜索到的值,比当前最优解还不如,那就剪掉。
代码:
#include
using namespace std;
const int maxn=20;
double dis_count(double x1,double y1,double x2,double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int n,vis[maxn];
double ax[maxn],ay[maxn],ans=999999999999999.9,dis[maxn][maxn];
void dfs(int step,int now,double res)
{
if(res>ans)return;//剪枝
if(step==n)
{
ans=min(ans,res);
return;
}
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
vis[i]=1;
dfs(step+1,i,res+dis[now][i]);
vis[i]=0;
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>ax[i]>>ay[i];
memset(dis,0,sizeof(dis));
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
if(i==j)continue;
dis[i][j]=dis_count(ax[i],ay[i],ax[j],ay[j]);
}
}
dfs(0,0,0.0);
cout.setf(ios::fixed);
cout<