P1506 拯救oibh总部
读字符千万注意会不会读到换行符!!
染色问题直接在地图外面多加一圈可染色的,再从这个圈的顶点开始染,以确保dfs能染到边边角角
#include
#include
char ch;
int n,m,map[505][505];
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};
void dfs(int x,int y)
{
if(x<0||x>n+1||y<0||y>m+1||map[x][y]) return;
map[x][y]=2;
for(int i=0;i<=3;i++)
{
dfs(x+dx[i],y+dy[i]);
}
}
int main()
{
int ans=0;
memset(map,0,sizeof(map));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf(" %c",&ch);
//赋值完后打印地图有误,调试后才发现在确定长宽之后多读了一个换行符
//!!!!
ch=='0'?map[i][j]=0:map[i][j]=1;
}
}
dfs(0,0);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(!map[i][j]) ans++;
}
}
printf("%d",ans);
return 0;
}
P1605 迷宫
超基础dfs,一交全部MLE,本来开了个10000的结构体,删了,还是MLE,最后把数组(才开到10000啊)改成10终于过了(
#include
#include
int ans=0;
int n,m,t;
int begin_x,begin_y,final_x,final_y;
int map[10][10];
int dx[4]={-1,1,0,0};
int dy[4]={0,0,1,-1};
void dfs(int x,int y)
{
if(x==final_x&&y==final_y) {ans++;return;}
else{
map[x][y]=2;
for(int j=0;j<=3;j++){
if(x+dx[j]<1||y+dy[j]<1||x+dx[j]>n||y+dy[j]>m) continue;
if(map[x+dx[j]][y+dy[j]]==0){
map[x+dx[j]][y+dy[j]]=2;
dfs(x+dx[j],y+dy[j]);
map[x+dx[j]][y+dy[j]]=0;
}
}
}
}
int main()
{
int x,y;
memset(map,0,sizeof(map));
scanf("%d%d%d",&n,&m,&t);
scanf("%d%d%d%d",&begin_x,&begin_y,&final_x,&final_y);
for(int i=1;i<=t;i++)
{
scanf("%d%d",&x,&y);
map[x][y]=1;
}
dfs(begin_x,begin_y);
printf("%d",ans);
return 0;
}
P1101 单词方阵
用一个标记用二维数组vis,将成单词的格子都标记为1,用于输出
先找y,再向y的八个方向找i,找到i就记录方向,进入深搜
用ans计数,同时用来更新下一个要匹配的字母(注意要写a+1,不能写a++!!)
因为不能找到一个就标记一个(因为可能某条路其实不能成词),所以先用一个结构体记下匹配的(x,y),确定成词之后再用它作标记
#include
#include
struct node
{
int x,y;
}c[110];
char map[101][101];
char word[]="yizhong";
int vis[101][101];
int dirx,diry;// 可行的方向变量
void dfs(int x,int y,node c[],int ans)
{
if(ans==7){
for(int i=0;i<7;i++)
vis[c[i].x][c[i].y]=1;
}
else{
if(ans==6||map[x][y]==word[ans]){
c[ans].x=x;c[ans].y=y;
dfs(x+dirx,y+diry,c,ans+1);//千万不能写a++!!
}
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%s",map[i]);
memset(vis,0,sizeof(vis));
for(int x=0;x<n;x++){
for(int y=0;y<n;y++){
if(map[x][y]=='y'){//找到'y'后向y的八个方向找i
for(int dx=-1;dx<=1;dx++){
for(int dy=-1;dy<=1;dy++){
if(map[x+dx][y+dy]=='i'){
//记录方向
dirx=dx;
diry=dy;
dfs(x,y,c,0); //从‘y’开始深搜
}
}
}
}
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(vis[i][j]) printf("%c",map[i][j]);
else printf("*");
}
printf("\n");
}
return 0;
}
P1036 选数
本题要求的操作分两部分:
一是素数判定(很常用的样子)
二是深度优先搜索
卡了将近半个月之后菜鸡终于看懂了题解(
画图辅助+调试看计算过程
递归在此时展现了它反复横跳(竖跳XD)的魅力(
sum的用处是在调试了两遍之后才后知后觉地反应过来。主要是因为纠结代码运行纠结得太久,以至于忘记了题目本来是要让我求什么数的
之后做点其他深搜题加强理解
//解法一:利用一个for循环来确保遍历了所有k个加数的组合
#include
#include
int x[20];
int n,k;
bool isprime(int n){
int s=sqrt(double(n));
for(int i=2;i<=s;i++){
if(n%i==0)return false;
}
return true;
}
int solve(int leftnum,int summing,int start,int end)
//还要加上的数的个数,已经得到的和,在哪一个范围内选择下一个数
{
if(leftnum==0) return isprime(summing);
//要求的个数已经加完,一次深搜结束,返回的结果直接送去判定
//切勿把这一条放在最后,否则sum直接清零。
int sum=0;
for(int i=start;i<=end;i++){// 开始/继续深搜
sum+=solve(leftnum-1,summing+x[i],i+1,end);
//在加数的过程中范围不断收窄
//sum用于统计为素数的和的个数
//由于素数判定函数用的是bool类型,当其为真时此处会返回1
}
return sum;
}
int main()
{
scanf("%d%d",&n,&k);
for(int j=0;j<n;j++){
scanf("%d",&x[j]);
}
printf("%d",solve(k,0,0,n-1));//注意此处设定的右边界与数组下标的关系
return 0;
}
//解法二:对于待选数x[i],选择加上,或者不加上
#include
#include
int x[20];
int n,k;
int sum=0;
int isprime(int n){//判断是否质数
int s=sqrt(double(n));
for(int i=2;i<=s;i++){
if(n%i==0)return 0;
}
return 1;
}
//我需要实现遍历每一条支路,而不是有一个解就直接结束
void solve(int leftnum,int summing,int i)
{
if(leftnum==0||i==n){
if(isprime(summing)&&leftnum==0) sum++;
//leftnum==0的条件千万别漏,因为情况可能是已经加到数组最后一个数而加数数量还不够
return;//就是这个!!直接返回上一层!!
}
solve(leftnum-1,summing+x[i],i+1);
solve(leftnum,summing,i+1);
return;//返回上一层
}
int main()
{
scanf("%d%d",&n,&k);
for(int j=0;j<n;j++){
scanf("%d",&x[j]);
}
solve(k,0,0);
printf("%d",sum);
return 0;
}
解法二走了很多弯路。
首先这个方法和白书上例题很像,但例题只要求判断“能否”,故用了bool类型的函数,只要找到一个解就层层上报好消息直接结束,没找到解才返回上一层继续往下找。
但本题要求遍历所有组合,难住我的是我应该要写一个什么类型的函数,每一次dfs的终点应该向上一层返回什么玩意才能做到回溯的效果。
然后是void,一次dfs完毕直接return就完事了。
emmmm…吸取教训,继续努力
题目:给定任意正整数n(没有范围其实是不行的,这里姑且认为50以内吧),输出1-n的所有可能的排列并输出种数
(其实是阿萧的C语言作业(?????
#include
int a[500];
int ans=0;
int n;
void swap(int i,int j){
int t=a[i];
a[i]=a[j];
a[j]=t;
}
void dfs(int start,int end){
if(start==n){
for(int i=1;i<=n;i++)
printf("%d",a[i]);
printf("\n");
ans++;
}
for(int i=start;i<=end;i++){
swap(start,i);
dfs(start+1,end);
swap(start,i);
}
}
int main()
{
scanf("%d",&n);
//创建一个包含1至n所有数字的数组
for(int i=1;i<=n;i++){
a[i]=i;
}
dfs(1,n);
printf("%d",ans);
return 0;
}
要点:
1.for循环保证了第i个数能与其他位置的数依次交换,且保证第i层仅做第i个数与其他数的交换。
2.数的交换发生在每一次DFS开始之前,为保证能回到原来状态做另外的交换,每次DFS结束后都要换回去。
3.不交换本身也是全排列的一种。
还有一个比较容易想到的解法:
对于当前待填的位置,尝试所有未尝试过的数
#include
void dfs(int n,int*a,int pos){
//n用于判断序列是否已经填完,数组a为已填数列,pos为当前待填位置
if(pos==n){
for(int i=0;i<n;i++) printf("%d",a[i]);
printf("\n");
}
else
for(int i=1;i<=n;i++){ //尝试填入各种数字
int ok=1;
for(int j=0;j<pos;j++)
//j
//当条件为j
if(a[j]==i) ok=0; //判断当前挑选的数字是否已填过
if(ok) {
a[pos]=i;
dfs(n,a,pos+1);
}
}
}
int main()
{
int n;
int a[1000]={0};
scanf("%d",&n);
dfs(n,a,0);
return 0;
}
UVA524 素数环
一个长度为n的数组,规定第一个为1,剩下n-1个格子就用2-n的正整数逐个试填,填完后特判检查,输出。
注意格式:最后一个数字后面不能有空格,每组输出之间要有空行
#include
#include
int a[17];
int n;
bool isprime(int n){
int s=sqrt(double(n));
for(int i=2;i<=s;i++){
if(n%i==0)return false;
}
return true;
}
void dfs(int i){
if(i==n+1&&isprime(a[i-1]+1)){
printf("%d",a[1]);
for(int x=2;x<=n;x++) printf(" %d",a[x]);
printf("\n");
}
else{
for(int j=2;j<=n;j++){
int ok=1;
for(int k=1;k<=n;k++){
if(j==a[k]) {
ok=0;
break;
}
}
if(ok){
if(isprime(j+a[i-1])) {
a[i]=j;
dfs(i+1);
a[i]=0;
//!!!注意此处要清除格子,才能回溯试填该格子的下一个数字!
//否则输出只有一种排列
}
}
}
}
}
int main()
{
int i=0;
int flag=0;
while(scanf("%d",&n)!=EOF){
if(flag) printf("\n");
flag=1;
printf("Case %d:\n",++i);
a[1]=1;
dfs(2);
}
return 0;
}
虽然紫书上的写法要简洁得多,但毕竟是第一个独立流畅写完的dfs,还是要表扬一下自己