回溯算法,说的再通俗一点,可以叫做回退算法,对于一条走不下去的路,没有必要再继续往下走,回到当前点的上一个点,再从上一个点进行判断,如果说上一个点还有其他路径,则继续查找;否则,继续回溯。
回溯结束的条件:1.找到所求问题的解(此时可能会有两种情况,其一:找到一组解就可以;其二:可能还有其他解或者说目前所求的不是问题的最优解,此时并不能终止查询,还要继续)。2.所有可能的路线都已经判断完毕。
回溯算法的时间优化:对于回溯算法来说,如何优化时间,是最令人头疼的问题,因为算法本身的设计可能包含很多无意义的搜索路径,如果从这条路径上继续搜的话,会有很大的时间开销,当然,此时也会大量的消耗内存。回溯算法的优化,即为剪枝,对于剪枝,通俗的说就是把所有的没有必要的搜索路径都给剔除。当然,这个需要自己慢慢积累才可以。
由于回溯主要是针对于图来说,毕竟见到的此类问题最多的也就是图。下面从图的角度来说。
以例为证
福克斯在玩一款手机解迷游戏,这个游戏叫做”两点”。基础级别的时候是在一个n×m单元上玩的。像这样:
每一个单元有包含一个有色点。我们将用不同的大写字母来表示不同的颜色。
这个游戏的关键是要找出一个包含同一颜色的环。看上图中4个蓝点,形成了一个环。一般的,我们将一个序列 d1,d2,...,dk 看成一个环,当且仅当它符合下列条件时:
1. 这k个点不一样,即当 i≠j时, di 和 dj不同。
2. k至少是4。
3. 所有的点是同一种颜色。
4. 对于所有的 1≤i≤k-1: di 和 di+1 是相邻的。还有 dk 和 d1 也应该相邻。单元 x 和单元 y 是相邻的当且仅当他们有公共边。
当给出一幅格点时,请确定里面是否有环。
单组测试数据。 第一行包含两个整数n和m (2≤n,m≤50):板子的行和列。 接下来n行,每行包含一个有m个字母的串,表示当前行每一个点的颜色。每一个字母都是大写字母。
如果有环输出Yes,否则输出No。
3 4 AAAA ABCA AAAA 3 4 AAAA ABCA AADA
Yes No
#include
using namespace std;
char a[111][111];
bool vis[111][111],flag;
int n,m;
int dir[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};
bool judge(int x,int y,int xx,int yy, char c)
{
for(int i = 0; i < 4; i ++)
{
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if(tx >= 0 && tx < n && ty >= 0 && ty < m && tx == xx && ty == yy && a[tx][ty] == c)
return true;
}
return false;
}
void dfs(int x,int y,int xx,int yy, char c,int num)
{
if(judge(x,y,xx,yy,c) && num >= 3)
{
flag = 1;
return;
}
for(int i = 0; i < 4; i ++)
{
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if(tx >= 0 && tx < n && ty >= 0 && ty < m && a[tx][ty] == c && !vis[tx][ty])
{
vis[tx][ty] = 1;
++ num;
dfs(tx,ty,xx,yy,c,num);
if(flag)
{
return;
}
-- num;
vis[tx][ty] = 0;
}
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
memset(vis,0,sizeof(vis));
memset(a,0,sizeof(a));
flag = 0;
for(int i = 0; i < n; i ++)
scanf("%s",a[i]);
for(int i = 0; i < n && !flag; i ++)
{
for(int j = 0; j < m && !flag; j ++)
{
memset(vis,0,sizeof(vis));
vis[i][j] = 1;
dfs(i,j,i,j,a[i][j],0);
}
}
if(!flag)
printf("No\n");
else
printf("Yes\n");
}
return 0;
}
.X
.X.
#include
#include
#include
#include
using namespace std;
int n,MAX;
bool vis[5][5];
char Map[5][5];
bool judge(int x,int y) ///以点(x,y)为起点,遍历四个方向,每个方向都遍历完
{
if(Map[x][y] == 'X')
return false;
for(int i = x + 1; i < n; i ++)
{
if(Map[i][y] == 'X')
break;
if(vis[i][y])
return false;
}
for(int i = x - 1; i >= 0; i --)
{
if(Map[i][y] == 'X')
break;
if(vis[i][y])
return false;
}
for(int i = y + 1; i < n; i ++)
{
if(Map[x][i] == 'X')
break;
if(vis[x][i])
return false;
}
for(int i = y - 1; i >= 0; i --)
{
if(Map[x][i] == 'X')
break;
if(vis[x][i])
return false;
}
return true;
}
void DFS(int pos)
{
int r = pos / n;
int c = pos % n;
if(pos >= n * n)
{
int sum = 0; ///统计放的个数
for(int i = 0; i < n; i ++)
for(int j = 0; j < n; j ++)
if(vis[i][j])
++ sum;
MAX = max(MAX,sum);
return;
}
if(judge(r,c)) ///判断该行是不是可以放置
{
///可以放置的话也有两种选择,放或者不放
///放
vis[r][c] = 1;
DFS(pos + 1);
vis[r][c] = 0; ///回溯
///不放
DFS(pos + 1);
}
else ///不可以放置
DFS(pos + 1);
return;
}
int main()
{
while(scanf("%d",&n) && n)
{
for(int i = 0; i < n; i ++)
scanf("%s",Map[i]);
memset(vis,0,sizeof(vis));
MAX = -0x3f3f3f;
DFS(0);
printf("%d\n",MAX);
}
return 0;
}