普通DFS
如题
例题:P1605 迷宫
原题地址
迷宫 【问题描述】
给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过。给定起点坐标和
终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。在迷宫
中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。
输入样例 输出样例
【数据规模】
1≤N,M≤5
输入输出格式
输入格式:
【输入】
第一行N、M和T,N为行,M为列,T为障碍总数。第二行起点坐标SX,SY,终点
坐标FX,FY。接下来T行,每行为障碍点的坐标。
输出格式:
【输出】
给定起点坐标和终点坐标,问每个方格最多经过1次,从起点坐标到终点坐标的方
案总数。
输入输出样例
输入样例#1:
2 2 1
1 1 2 2
1 2
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int m,n,t,a,b,sx,sy,fx,fy,ans=0;
int qq[10][10];
void dfs(int x,int y)
{
if(x==fx&&y==fy)
{
ans++;
return;
}
else
{
if(qq[x][y]==0) return;
else
{
qq[x][y]=0;//走过,如果不判定走过大概率TLE
dfs(x,y+1);
dfs(x,y-1);
dfs(x+1,y);
dfs(x-1,y);
qq[x][y]=1;//回溯
}
}
return;
}
int main()
{
int i,j;
memset(qq,0,sizeof(qq));
scanf("%d%d%d",&m,&n,&t);
scanf("%d%d%d%d",&sx,&sy,&fx,&fy);
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
qq[i][j]=1;
for(i=1;i<=t;i++)
{
scanf("%d%d",&a,&b);
qq[a][b]=0;
}
dfs(sx,sy);
if(qq[fx][fy]==0) ans=0;
printf("%d\n",ans);
return 0;
}
例题:P1219 八皇后
原题地址
题目描述
检查一个如下的6 x 6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个棋子,如下:
行号 1 2 3 4 5 6
列号 2 4 6 1 3 5
这只是跳棋放置的一个解。请编一个程序找出所有跳棋放置的解。并把它们以上面的序列方法输出。解按字典顺序排列。请输出前3个解。最后一行是解的总个数。
输入输出格式
输入格式:
一个数字N (6 <= N <= 13) 表示棋盘是N x N大小的。
输出格式:
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
输入输出样例
输入样例#1:
6
输出样例#1: 复制
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[100],b[100],c[100],d[100];
int total;
int n;
void print()
{
if(total<=2)//
{
for(int k=1;k<=n;k++)
cout<<a[k]<<" ";
cout<<endl;
}
total++;
}
void queen(int i)
{
if(i>n)
{
print();
return;
}
else
{
for(int j=1;j<=n;j++)
{
if((!b[j])&&(!c[i+j])&&(!d[i-j+n]))
{
a[i]=j;
b[j]=1;
c[i+j]=1;//占领对角线
d[i-j+n]=1;//占领对角线
queen(i+1);
b[j]=0;
c[i+j]=0;
d[i-j+n]=0;
}
}
}
}
int main()
{
cin>>n;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
queen(1);
cout<<total<<endl;
return 0;
}
隐藏的DFS
例题:P1218 [USACO1.5]特殊的质数肋骨 Superprime Rib
原题地址
题目描述
农民约翰的母牛总是产生最好的肋骨。你能通过农民约翰和美国农业部标记在每根肋骨上的数字认出它们。农民约翰确定他卖给买方的是真正的质数肋骨,是因为从右边开始切下肋骨,每次还剩下的肋骨上的数字都组成一个质数,举例来说: 7 3 3 1 全部肋骨上的数字 7331是质数;三根肋骨 733是质数;二根肋骨 73 是质数;当然,最后一根肋骨 7 也是质数。 7331 被叫做长度 4 的特殊质数。写一个程序对给定的肋骨的数目 N (1<=N<=8),求出所有的特殊质数。数字1不被看作一个质数。
输入输出格式
输入格式:
单独的一行包含N。
输出格式:
按顺序输出长度为 N 的特殊质数,每行一个。
输入输出样例
输入样例#1:
4
输出样例#1:
2333
2339
2393
2399
2939
3119
3137
3733
3739
3793
3797
5939
7193
7331
7333
7393
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
int q,n;
int fun(int t)
{
int i;
for(i=2;i<=sqrt((double)t);i++)
{
if(t%i==0) return 0;
}
return 1;
}
void dfs(int x,int i)
{
int j;
if(i==n)
{
printf("%d\n",x);
return;
}
else
{
for(j=1;j<=9;j++)
{
if(fun(x*10+j)) dfs(x*10+j,i+1);
}
}
return;
}
int main()
{
int i;
scanf("%d",&n);
dfs(2,1);
dfs(3,1);
dfs(5,1);
dfs(7,1);
return 0;
}
例题:P1215 [USACO1.4]母亲的牛奶 Mother’s Milk
原题地址
题目描述
农民约翰有三个容量分别是A,B,C升的桶,A,B,C分别是三个从1到20的整数, 最初,A和B桶都是空的,而C桶是装满牛奶的。有时,农民把牛奶从一个桶倒到另一个桶中,直到被灌桶装满或原桶空了。当然每一次灌注都是完全的。由于节约,牛奶不会有丢失。
写一个程序去帮助农民找出当A桶是空的时候,C桶中牛奶所剩量的所有可能性。
输入输出格式
输入格式:
单独的一行包括三个整数A,B和C。
输出格式:
只有一行,升序地列出当A桶是空的时候,C桶牛奶所剩量的所有可能性。
输入输出样例
输入样例#1:
[输入1]
8 9 10
[输入2]
2 5 10
输出样例#1:
[输出1]
1 2 8 9 10
[输出2]
5 6 7 8 9 10
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int a,b,c;
int t2[21][21][21],t[21];//t2可判断是否曾经达到该状态。
void milk(int x,int y,int z)//搜索部分
{
if(t2[x][y][z]) return;
if(!x) t[z]=1;
t2[x][y][z]=1;
y+z>b?milk(x,b,y+z-b):milk(x,y+z,0);
x+z>a?milk(a,y,x+z-a):milk(x+z,y,0);
z+y>c?milk(x,z+y-c,c):milk(x,0,z+y);
x+y>a?milk(a,x+y-a,z):milk(x+y,0,z);
y+x>b?milk(y+x-b,b,z):milk(0,y+x,z);
z+x>c?milk(z+x-c,y,c):milk(0,y,z+x);
return;
}
int main()
{
scanf("%d%d%d",&a,&b,&c);
memset(t2,0,sizeof (t2));
milk(0,0,c);
int q=1;
for(int i=0;i<=20;i++)
{
if(t[i]&&q==0) printf(" %d",i);
if(t[i]&&q==1)
{
printf("%d",i);
q=0;
}
}
printf("\n");
return 0;
}
染色问题
染色问题有时需要更加细化的处理。
例题:P1506 拯救oibh总部
原题地址
题目描述
oibh被突来的洪水淹没了>.<还好oibh总部有在某些重要的地方起一些围墙,用号表示,而一个封闭的号区域洪水是进不去的……现在给出oibh的围墙建设图,问oibh总部没被淹到的重要区域(由"0"表示)有多少。
输入输出格式
输入格式:
第一行是两个数,x和y(x,y<=500)
第二行及以下是一个由和0组成的xy的图。
输出格式:
输出没被水淹没的oibh总部的“0”的数量。
输入输出样例
输入样例#1:
样例输入1
4 5
00000
0000
000
0000
样例输入2
5 5
00*
0
00*
输出样例#1:
样例输出1
1
样例输出2
5
代码:
30分代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
using namespace std;
char qq[550][550];
int dx[4]={0,0,1,-1};
int dy[4]={-1,1,0,0};
int n,m,ans=0;
void dfs(int a,int b)
{
int i;
if(qq[a][b]=='*'||a<0||b<0||a>n+1||b>n+1||qq[a][b]=='+') return;
else qq[a][b]='+';
for(i=0;i<4;i++)
dfs(a+dx[i],b+dy[i]);
return;
}
int main()
{
int i,j;
memset(qq,0,sizeof(qq));
scanf("%d%d",&n,&m);
getchar();
for(i=1;i<=n;i++)
scanf("%s",qq[i]+1);
dfs(0,0);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(qq[i][j]=='0') ans++;
printf("%d\n",ans);
return 0;
}
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
using namespace std;
char qq[550][550];
int dx[4]={0,0,1,-1};
int dy[4]={-1,1,0,0};
int n,m,ans=0;
void dfs(int a,int b)
{
int i,tx,ty;
qq[a][b]='*';
for(i=0;i<4;i++)
{
tx=a+dx[i];
ty=b+dy[i];
if(qq[tx][ty]=='0')
dfs(tx,ty);
}
}
int main()
{
int i,j;
memset(qq,0,sizeof(qq));
scanf("%d%d",&n,&m);
getchar();
for(i=1;i<=n;i++)
scanf("%s",qq[i]+1);
for(i=1;i<=n;i++)
{
if(qq[i][1]=='0')
dfs(i,1);
if(qq[i][m]=='0')
dfs(i,m);
}
for(i=1;i<=m;i++)
{
if(qq[1][i]=='0')
dfs(1,i);
if(qq[n][i]=='0')
dfs(n,i);
}
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(qq[i][j]=='0') ans++;
printf("%d\n",ans);
return 0;
}
反向DFS
例题:P1913 L国的战斗之伞兵
原题地址
题目描述
为了在敌国渗透作战,指挥官决定:派出伞兵前往敌国!然而敌国的风十分强烈,能让伞兵在同一高度不停转悠,直到被刮到一个无风区……(可怜的小兵)
输入输出格式
输入格式:
第一行:n、m两个正整数,表示敌国的大小。
以下n行,每行m个字符,“u”表示风向北吹;“d”表示风向南吹;“l”表示风向西吹;“r”表示风向东吹;“o”表示无风。(上北下南,左西右东)
输出格式:
一个数:表示有几个点可以放下伞兵。
输入输出样例
输入样例#1:
5 5
rrrrr
rdddr
rroll
uuuuu
uuuuu
输出样例#1:
19
数据范围:
1≤n≤1000,1≤m≤1000.
代码:
其实这题很简单,找对方法是关键。
找到无风的o点,然后开始推路径,然后将能到的点设为true,最后统计TRUE的个数就AC。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int qq[1010][1010];
char ch[1010][1010];
int n,m;
void dfs(int a,int b)
{
qq[a][b]=1;
if(ch[a-1][b]=='d') dfs(a-1,b);
if(ch[a+1][b]=='u') dfs(a+1,b);
if(ch[a][b-1]=='r') dfs(a,b-1);
if(ch[a][b+1]=='l') dfs(a,b+1);
}
int main()
{
int i,j,ans=0;
scanf("%d%d",&n,&m);
memset(ch,0,sizeof(ch));
getchar();
for(i=1;i<=n;i++)
scanf("%s",ch[i]+1);
memset(qq,0,sizeof(qq));
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
if(ch[i][j]=='o'&&qq[i][j]==0) dfs(i,j);
}
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(qq[i][j]==1) ans++;
printf("%d\n",ans);
return 0;
}
DFS与打表
例题:P1010 幂次方
原题地址
题目描述
任何一个正整数都可以用22的幂次方表示。例如
137=2^(7)+8+1;
137可表示为:
2(2(2)+2+2(0))+2(2+2(0))+2(0)
1315最后可表示为:
2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)
输入输出格式
输入格式:
一个正整数n(n≤20000)、。
输出格式:
符合约定的n的0,2表示(在表示中不能有空格)
输入输出样例
输入样例#1:
1315
输出样例#1:2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)
代码:
8以下的数打表。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
using namespace std;
int n;
int qq[20];
void init()
{
int i;
qq[0]=1;
for(i=1;i<=19;i++)
qq[i]=qq[i-1]*2;
}
void dabiao(int x)
{
if(x==7) printf("2(2)+2+2(0)");
if(x==6) printf("2(2)+2");
if(x==5) printf("2(2)+2(0)");
if(x==4) printf("2(2)");
if(x==3) printf("2+2(0)");
if(x==2) printf("2");
if(x==1) printf("2(0)");
}
void dfs(int x)
{
int i;
for(i=0;qq[i]<=x;i++);
i--;
x-=qq[i];
printf("2(");
if(i>7)
{
dfs(i);
}
else dabiao(i);
printf(")");
if(x>0)
{
printf("+");
if(x>7) dfs(x);
else dabiao(x);
}
else return;
}
int main()
{
init();
scanf("%d",&n);
if(n>7) dfs(n);
else dabiao(n);
printf("\n");
return 0;
}