深搜的基本框架:
void dos(int step)
{
判断边界
尝试每一种可能for
{
继续下一步 dfs(step+1)
}
返回
}
在做了几道比较基础的深搜的题,对深搜有了点感悟,现在已经遇到了两种类型,一个是类似于全排列的题,如六角填数,或基本的全排列输出,用六角填数来举个例子,这个思想就像是有12个格子,手中有12个数字,把数字放入格子满足某种条件,这类题深搜思想的终止条件就是stpe==最后一个格子+1,其余的就用visit,dfs和回溯,还有一种题就是表格题,这种题就要具体分析了,对于n皇后问题,那么我们就定位行列,对于炸弹人我们就定位格子数,而对于石油合并和red and black 总是找不到终止条件,这个一直很迷糊,感觉其他的n皇后,或者是炸弹人,对于他们的行和列,放不放,如果放放几个,都是不确定的,所以每一个格子都有机会放,或者不放,而对于石油合并和red and black他们都不是放东西,而是找东西,找个数,于是他们只要找出满足的个数就可以了,并不需要从第一个格子到最后一个格子全部的去dfs
在每一次的搜索标记的时候,应注意使每一个例子的标记都出实话,如全局变量book[25][25],记得在主函数调用c++的csting memset(book,0,sizeof(book))使得每一个例子最开始都未被标记
1045题
A blockhouse is a small castle that has four openings through which to shoot. The four openings are facing North, East, South, and West, respectively. There will be one machine gun shooting through each opening.
Here we assume that a bullet is so powerful that it can run across any distance and destroy a blockhouse on its way. On the other hand, a wall is so strongly built that can stop the bullets.
The goal is to place as many blockhouses in a city as possible so that no two can destroy each other. A configuration of blockhouses is legal provided that no two blockhouses are on the same horizontal row or vertical column in a map unless there is at least one wall separating them. In this problem we will consider small square cities (at most 4x4) that contain walls through which bullets cannot run through.
The following image shows five pictures of the same board. The first picture is the empty board, the second and third pictures show legal configurations, and the fourth and fifth pictures show illegal configurations. For this board, the maximum number of blockhouses in a legal configuration is 5; the second picture shows one way to do it, but there are several other ways.
Your task is to write a program that, given a description of a map, calculates the maximum number of blockhouses that can be placed in the city in a legal configuration.
4 .X.. .... XX.. .... 2 XX .X 3 .X. X.X .X. 3 ... .XX .XX 4 .... .... .... .... 0
5 1 5 2 4
<span style="font-size:14px;">#include <iostream>
using namespace std;
char a[5][5];
int max1=0,n;
int check(int x,int y)
{
int i;
for(i=y+1;i<=n&&a[x][i]!='X';i++)
{
if(a[x][i]=='@')
return 0;
}
for(i=y-1;i>0&&a[x][i]!='X';i--)
{
if(a[x][i]=='@')
return 0;
}
for(i=x+1;i<=n&&a[i][y]!='X';i++)
{
if(a[i][y]=='@')
return 0;
}
for(i=x-1;i>0&&a[i][y]!='X';i--)
{
if(a[i][y]=='@')
return 0;
}
return 1;
}
void dfs(int num,int boom)
{
if(num==n*n)
{
if(boom>max1)
max1=boom;
return;
}
<span style="color:#ff0000;"> int x;int y;
x=num/n+1;
y=num%n+1;
if(a[x][y]=='.')
{
if(check(x,y)==1)
{
a[x][y]='@';
dfs(num+1,boom+1);
a[x][y]='.';
}
}
dfs(num+1,boom);
</span>
}
int main()
{
int i,j;
while(cin>>n)
{
if(n==0)
break;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
cin>>a[i][j];
}
}
max1=0;
dfs(0,0);
cout<<max1<<endl;
}
}</span>
<span style="font-size:14px;">#include <iostream>
using namespace std;
int a[51][51],book[51][51]={0};
int n,m,tx,ty,q,p,k,mia=99999999;
void dfs(int x,int y,int step)
{
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
if(x==p && y==q)
{
if(step<mia)
mia=step;
return ;
}
<span style="color:#ff0000;">for(k=0;k<=3;k++)
{
tx=x+next[k][0];
ty=y+next[k][1];
if(tx<1 || tx>n || ty<1 ||ty>m)
continue;
if(a[tx][ty]==0 && book[tx][ty]==0)
{
book[tx][ty]=1;
dfs(tx,ty,step+1);
book[tx][ty]=0;
}
}
}</span>
int main()
{
int i,j,x1,y1;
cin>>n>>m;
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
cin>>a[i][j];
}
}
cin>>x1>>y1>>p>>q;
book[x1][y1]=1;
dfs(x1,y1,0);
cout<<mia;
}救啊哈
</span>
这两个红色代码就是尝试每一种可能,第一段红色代码通过假设在每一个红色格子上先放上炸弹,再通过num的数量来依次往从第一个放炸弹的地方往后依次尝试放炸弹,例如刚开始num=0,则表示从1,1的位置放炸弹,然后递归可以求出如果第一个炸弹在1,1这个位置,最多可以放多少个炸弹,然后num=1,表示1,2这个位置最开始放炸弹,依次类推,
Write a program to count the number of black tiles which he can reach by repeating the moves described above.
There are H more lines in the data set, each of which includes W characters. Each character represents the color of a tile as follows.
'.' - a black tile
'#' - a red tile
'@' - a man on a black tile(appears exactly once in a data set)
6 9 ....#. .....# ...... ...... ...... ...... ...... #@...# .#..#. 11 9 .#......... .#.#######. .#.#.....#. .#.#.###.#. .#.#..@#.#. .#.#####.#. .#.......#. .#########. ........... 11 6 ..#..#..#.. ..#..#..#.. ..#..#..### ..#..#..#@. ..#..#..#.. ..#..#..#.. 7 7 ..#.#.. ..#.#.. ###.### ...@... ###.### ..#.#.. ..#.#.. 0 0
#include<stdio.h>
#include <cstring>
char s[25][25];
int a[4]={0,0,1,-1};
int b[4]={1,-1,0,0};
int book[25][25]={0};
int m,n,sum;
void bfs(int x,int y)
{
int k,v,t;
for(k=0;k<4;k++)
{
v=x+a[k];
t=y+b[k];
if(s[v][t]=='.'&&v>=1&&v<=n&&t>=0&&t<m && book[v][t]==0)
{
sum++;
book[v][t]=1;
bfs(v,t);
}
}
}
int main()
{
int i,j;
while(scanf("%d%d",&m,&n)&&(m+n))
{
memset(book,0,sizeof(book));
for(i=1;i<=n;i++)
scanf("%s",s[i]);
for(i=1,sum=1;i<=n;i++)
{
for(j=0;j<m;j++)
{
if(s[i][j]=='@')
{
book[i][j]=1;
bfs(i,j);
}
}
}
printf("%d\n",sum);
}
return 0;
}
这个题不用回溯和返回,为什么呢,我觉的假如从@的左边开始上下左右的搜索和标记,肯定会有一些满足条件的点没有被标记,在从@的上边开始上下左右的标记,再从下边,再从右边,感觉其实限制它的条件就是#和边界,只要不是他们两个,就上下左右的搜索和标记,等到递归完成后,就求出了值
你的任务是,对于给定的N,求出有多少种合法的放置方法。
1 8 5 0
1 92 10
#include<stdio.h>
#include<math.h>
int x[15];//x数组表示放在该列的哪个位置,下标表示列,值表示放置在第几行
int sum, n;
bool place (int v)
{
int i;
for(i=1;i<v;++i)
{
if(x[i] == x[v] || abs(x[i]-x[v])==abs(i-v))//如果在同一行,或在对角线方向(为什么这里不判断是否为同一列呢?主要是因为x数组表示放在该列的哪个位置)
return 0;
}
return 1;
}
void backtrack(int v)
{
int i;
if(v>n) //把最后一个皇后放置成功后
sum++;
else
{
for(i=1;i<=n;++i) //找n个皇后
{
x[v]=i;
if(place(v))
{
backtrack(v+1);
}
}
}
}
int main()
{
int ans[15];
for(n = 1; n <= 10; ++n)
{
sum = 0;
backtrack(1);
ans[n] = sum;
}
while(scanf("%d",&n), n)
{
printf("%d\n",ans[n]);
}
return 0;
}
这道题的思想是假如有一个n*n的格子,在满足题意的情况下,每一行和每一列只能放一个皇后,所以该题就是先限制列然后判断行,如果该行该列不能放,就变成下一行的该列,知道所有列都被搜索万即v等于n+1
Input
先输入2个正整数n,m(1<=n,m<=50)。接着有n行,每行有m个字符。'@'表示该单元有石油,'*'则表示该单元没有石油。输入到文件结束。
Output
对于每组测试,输出最少需要架设几台采油机。Sample Input
2 2
@*
*@
2 2
@@
@@
Sample Output
2
1
<span style="font-size:14px;">#include <iostream>
#include <cstdio>
#include <cstring>
#define M 55
using namespace std;
char s[M][M];
int n, m, many;
int map[M][M];
int dfs(int i, int j){
map[i][j] = 1;
if(i+1<n&&s[i+1][j]=='@'&&map[i+1][j]==0){
dfs(i+1,j);
}
if(i-1>=0&&s[i-1][j]=='@'&&map[i-1][j]==0){
dfs(i-1,j);
}
if(j-1>=0&&s[i][j-1]=='@'&&map[i][j-1]==0){
dfs(i,j-1);
}
if(j+1<m&&s[i][j+1]=='@'&&map[i][j+1]==0){
dfs(i,j+1);
}
return 1;
}
int main()
{
while(scanf("%d%d", &n, &m)!=EOF){
many = 0;
memset(map, 0, sizeof(map));
for(int i = 0; i < n; i++){
scanf("%s", s[i]);
}
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(map[i][j] == 0 && s[i][j] == '@'){
many += dfs(i,j);
}
}
}
printf("%d\n", many);
}
return 0;
}
http://acm.hrbeu.edu.cn/index.php?act=problem&id=1001&cid=47</span>
这道题虽然很水,但是让我看见一个新的搜索代码方式,于是对于这个思想稍稍一改便是red and black的另一种ac代码就是优点慢
#include <iostream>
#include <cstring>
using namespace std;
char map[55][55];
int visit[55][55];
int n,m,sum;
void dfs(int i,int j)
{
visit[i][j]=1;
if(i+1<=n && map[i+1][j]=='.' && visit[i+1][j]==0)
{
sum++;
dfs(i+1,j);
}
if(i-1>=0 && map[i-1][j]=='.' && visit[i-1][j]==0)
{
sum++;
dfs(i-1,j);
}
if(j+1<=m && map[i][j+1]=='.' && visit[i][j+1]==0)
{
sum++;
dfs(i,j+1);
}
if(j-1>=0 && map[i][j-1]=='.' && visit[i][j-1]==0)
{
sum++;
dfs(i,j-1);
}
return ;
}
int main()
{
int i,j = 0,k=0;
while(cin>>m>>n)
{
if(n==0 && m==0)
break;
sum=0;
memset(visit,0,sizeof(visit));
memset(map,0,sizeof(map));
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
cin>>map[i][j];
}
}
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
if(map[i][j]=='@')
{
k=1;
break;
}
}
if(k==1)
break;
}
k=0;
sum++;
dfs(i,j);
cout<<sum<<endl;
}
}
全排列问题
第二段红代码则是通过逆时针顺序进行依次试探,如果走过则坐上标记在book,两个代码都别忘了回溯
数字的全排列
<span style="font-size:14px;">#include <iostream>
using namespace std;
int a[10],c[5];
int book[10],n=4;
void dfs(int step)
{
int i;//注意这个i必须是局部变量不能是全局变量
if(step==n+1)
{
for(i=1;i<=n;i++)
cout<<a[i]<<' ';
cout<<endl;
return ;
}
for(i=1;i<=n;i++)
{
if(book[i]==0)
{
a[step]=c[i];
book[i]=1;
dfs(step+1);
book[i]=0;
}
}
}
int main()
{
int j;
for(j=1;j<=4;j++)
cin>>c[j];
dfs(1);
}</span>
为什么i一定要是局部变量而不能是全局变量呢?我的理解是如果设为全局变量在输出完一次后,i的值为n+1而return之后,对于for循环i不满足小于等于n,所以跳出了循环,而如果设成局部变量,相当于对于每一个dfs都对应一个i,每次的i都是不同的
第7题:六角填数(12')
如图所示六角形中,填入1~12的数字。
使得每条直线上的数字之和都相同。
图中,已经替你填好了3个数字,请你计算星号位置所代表的数字是多少?
请通过浏览器提交答案,不要填写多余的内容。
快速解题思路:dfs 从上到下,从左到右从1到12标记每一个点。初始化1,8,3. 要求的地方点的标记为6.碰到1,2,12,就直接dfs下一个。
就类似于一个全排列,但是注意下到第1,2,12,stept的时候直接下一个,可惜自己太水,还是没有想到这点,想的太简单#include <iostream>
using namespace std;
int x[15],book[15];
void dfs(int step)
{
int i,k;
if(step == 1 || step == 2 || step == 12){
dfs(step+1);
return ;
}
if(step==13)
{
int b[6];
b[0]=x[1]+x[3]+x[6]+x[8];
b[1]=x[1]+x[4]+x[7]+x[11];
b[2]=x[2]+x[3]+x[4]+x[5];
b[3]=x[8]+x[9]+x[10]+x[11];
b[4]=x[2]+x[6]+x[9]+x[12];
b[5]=x[5]+x[7]+x[10]+x[12];
for(k=1;k<6;k++)
{
if(b[k-1]!=b[k])
return;
}
cout<<x[6];
return;
}
for(i=1;i<=12;i++)
{
if(book[i]==0)
{
x[step]=i;
book[i]=1;
dfs(step+1);
book[i]=0;
}
}
}
int main()
{
x[1]=1;x[2]=8;x[12]=3;
book[1]=1;book[8]=1;book[3]=1;
dfs(1);
}