您的位置:首页 > 其它

DFS(11)

2020-02-02 19:45 148 查看

DFS(主要还是靠大家慢慢理解)

说实话,写这种博客我也挺难的。。。
因为他的内容太不好理解了
也没得什么好说的,直接做题理解吧。

先来一个特别有意思的递归题。

传送门: 放苹果 POJ - 1664

#include <stdio.h>
using namespace std;
typedef long long ll;
int n,m,ans;
int DFS(int m,int n)
{
if(n==0||n==1)
return 1;
if(n>m)
return DFS(m,m);
else
return DFS(m-n,n)+DFS(m,n-1);

}
int main()
{ 	int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&m,&n);
printf("%d\n",DFS(m,n));

}
return 0;

}

传送门:Red and Black HDU - 1312

是不是感觉特熟悉,没错,上次写这个题用的是BFS,这次用的是DFS

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int xx,yy,x11,y11,ans;
char c[25][25];
int vis[25][25];
int dd[4][2]={1,0,-1,0,0,1,0,-1};
void DFS(int x,int y)
{
vis[x][y]=1;
if(x<0||y<0||x>=xx||y>=yy)
return ;
for(int i=0;i<4;i++)
{
int dx=x+dd[i][0];
int dy=y+dd[i][1];
if(dx>=0&&dy>=0&&dx<xx&&dy<yy&&vis[dx][dy]==0&&c[dx][dy]!='#')
{
ans++;
DFS(dx,dy);
}
}
}

int main()
{
while(~scanf("%d %d",&yy,&xx)&&yy&&xx)
{
ans=1;
memset(vis,0,sizeof vis);
for(int i=0;i<xx;i++)
{
for(int j=0;j<yy;j++)
{
cin>>c[i][j];
if(c[i][j]=='@')
{
x11=i;
y11=j;
}
}
}
DFS(x11,y11);
cout<<ans<<endl;
}
return 0;
}

传送门:Oil Deposits HDU - 1241

还是用了个老方法,把走过的变成‘*’,还可以再开一个数组进行标记。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int xx,yy,x11,y11,ans;
char c[110][110];
int vis[110][110];
int dd[8][2]={1,0,-1,0,0,1,0,-1,1,1,-1,-1,1,-1,-1,1};
void DFS(int x,int y)
{
c[x][y]='*';
for(int i=0;i<8;i++)
{
int dx=x+dd[i][0];
int dy=y+dd[i][1];
if(dx>=0&&dy>=0&&dx<xx&&dy<yy&&c[dx][dy]!='*')  //不走出边界
{
DFS(dx,dy);
}
}
}
int main()
{
while(~scanf("%d %d",&xx,&yy)&&yy&&xx)
{
ans=0;
for(int i=0;i<xx;i++)
cin>>c[i];
for(int i=0;i<xx;i++)
{
for(int j=0;j<yy;j++)
{
if(c[i][j]=='@')
{
DFS(i,j);
ans++;
}
}
}
cout<<ans<<endl;
}
return 0;

}

传送门:Fox And Two Dots CodeForces - 510B (涉及到新方法)

//#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long int ll;
int xx,yy,flag;
int vis[55][55];
char map[55][55];
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};

void dfs(int a,int b,int aa,int bb)
{
int k;
char c=map[a][b];
for(int k=0;k<4;k++)
{
int x=a+dx[k];
int y=b+dy[k];
if(map[x][y]==c&&x>=0&&y>=0&&x<xx&&y<yy)
{
if(x==aa&&y==bb) //因为是4个方向都要遍历,防止遍历时又退回上一步
continue;
if(vis[x][y])
{
flag=1;
return ;
}
vis[x][y]=1;
dfs(x,y,a,b);
}
}

}
int main()
{
while(~scanf("%d%d",&xx,&yy))
{

flag=0;
memset(vis,0,sizeof vis);
for(int i=0;i<xx;i++)
cin>>map[i];
for(int i=0;i<xx;i++)
{
for(int j=0;j<yy;j++)
{
if(!vis[i][j])
dfs(i,j,-1,-1);
if(flag)
break;
}
}
if (flag)
printf ("Yes\n");
else
printf ("No\n");
}
return 0;
}

N皇后问题 HDU - 2553 (超级经典,一定要好好感受里面的回溯思想)

当初不会这个题,然后就让N=4,然后就一直手跑2个多小时,最后终于跑出来了。。。

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
int N,cot;    //分别代表棋盘尺寸(皇后数量)和皇后可能成功摆放的结果总数
int size[11];//数组的下标表示行,对应的值表示列
int ans[11]; // 用于打表存放结果
void dfs(int n)
{
int flag;
if(n==N+1)  //当 最后一个皇后成功放置时返回.
{
cot++;   //成功的次数加一
return ;
}
for(int i=1;i<=N;i++)
{
size[n]=i;   //代表将第一个皇后放置在第1行第i列(或者说第n个皇后)
flag=1;      //放完之后就行标记
for(int j=1;j<n;j++)  //这个for循环用来检测 这个皇后的位置和之前的n个皇后的位置是否冲突
{
if(size[j]==i||((abs(n-j))==abs(size[j]-i)))  //判断条件
{
flag=0;
break;
}
}
if(flag)
dfs(n+1);   // 如果放置在第i列不冲突 则开始放下一个皇后   如果冲突 放在下一列继续判断
}

}
int main()
{
for( N=1;N<=11;N++)//因为本题时间限制,所以需要先打表
{
cot=0;    //临时存放结果
dfs(1);
ans[N]=cot;  // 将结果依次放入表中
}
while(cin >>N)
{
if(!N) break;
cout <<ans[N]<<endl;
}
return 0;
}

传送门:Tempter of the Bone HDU - 1010 (奇偶剪枝)

大体的题意就不说了,就是DFS+奇偶剪枝。在这个题里有一个很牛的方法:奇偶剪枝,不会这个基本做不出来这个题,这里就不给大家简绍了,大家直接百度,里面的大佬写的太好了。

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
int n,m,T,flag,ti;  //ti是步数
char c[10][10];
int vis[10][10];
int x_1,y_1,x2,y2;
int dd[4][2]={-1,0,1,0,0,1,0,-1};
void dfs(int x,int y)
{
if(x<0||y<0||x>=m||y>=n||ti>T)
return ;                   //超出范围或者步数超了直接就剪掉
if(flag)
return ;                   //找到答案就剪掉
if(x==x2&&y==y2&&ti==T)        //出口的判断
{
flag=1;
return ;
}
int dis=abs(T-ti-(abs(x2-x)+abs(y2-y)));   //传说中的奇偶剪枝
if(dis<0||dis%2)                            //我也是第一次用,感觉好厉害
return ;
vis[x][y]=1;
for(int i=0;i<4;i++)
{
int dx=x+dd[i][0];
int dy=y+dd[i][1];
if(dx>=0&&dy>=0&&dx<m&&dy<n&&!vis[dx][dy]&&c[dx][dy]!='X')
{
ti++;
dfs(dx,dy);
vis[dx][dy]=0;              //回溯思想
ti--;

}
}

}

int main()
{
ios::sync_with_stdio(false);
while(cin>>n>>m>>T)
{
ti=0;
flag=0;
memset(vis,0,sizeof vis);
if(n==0&&m==0&&T==0)
break;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
cin>>c[i][j];
if(c[i][j]=='S')
{
x_1=i;
y_1=j;
}
if(c[i][j]=='D')
{
x2=i;
y2=j;
}
}
}
dfs(x_1,y_1);
if(flag)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}

传送门:棋盘问题 POJ - 1321

上面那么难的问题都解决了,这个一定会。

//#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long int ll;
int n,k,m,sum;
char c[10][10];
int vis[10][10];
int a[10];

void dfs(int cur)
{
if(k==m)
{
sum++;
return ;
}
if(cur>=n)
return ;
for(int j=0;j<n;j++)
{
if(a[j]==0&&c[cur][j]=='#')
{
a[j]=1;
m++;
dfs(cur+1);
a[j]=0;
m--;
}
}
dfs(cur+1);
}

int main()
{
while(scanf("%d%d",&n,&k)&&n!=-1&&k!=-1)
{
sum=0;
memset(a,0,sizeof a);
for(int i=0;i<n;i++)
cin>>c[i];
dfs(0);
cout<<sum<<endl;
}
return 0;
}

传送门: Sudoku POJ - 2676

我的天哪,难死了,写了好长时间一直不对。。。
给一个CV代码,大家慢慢理解吧,。。。。

这道题的处理方式很巧妙。dfs中的参数只设置一个n(n就是代表着这是第几个数)就够了,就是当前的位置,在dfs中每次加一,如果当前位置不为0,就直接查找下一个位置。否则就从1~9中选一个数放进去,看同一行同一列以及同一小方格的同一行同一列有没有与之相同的,如果没有,就将当前位置的数更新成这个没有重复出现过的数。知道查找到n>=81的时候,即每个位置都查找完了,结束查找输出即可。注意在查找的过程中需要用到回溯,因为如果这一条路行不通而返回上一条路的时候,此时当前位置还应该是0.
n/9代表的就是当前位置的行,n%9代表的就是当前位置的列。
n/9/33表示的就是当前位置在小方格里的行,n%9/33代表的就是当前位置在小方格里的列。

#include<iostream>
#include<cstdio>
using namespace std;
int a[10][10],flag;
//    n/9代表的就是当前位置的行,n%9代表的就是当前位置的列。
//    n/9/3*3表示的就是当前位置在小方格里的行.
//   n%9/3*3代表的就是当前位置在小方格里的列。
bool check(int n,int now)
{
for(int i=0;i<9;i++)
{
int j=n%9;
if(a[i][j]==now)
return 0;
}
for(int j=0;j<9;j++)
{
int i=n/9;
if(a[i][j]==now)
return 0;
}
int di=n/9/3*3;
int dj=n%9/3*3;
for(int i =di;i<di+3;i++)
{
for(int j=dj;j<dj+3;j++)
{
if(a[i][j]==now)    return 0;
}
}
return 1;
}
int dfs(int n)
{
if(n>=81)
{
flag=1;
return 0;
}
if(a[n/9][n%9]!=0)    dfs(n+1);
else
{
for(int i=1;i<=9;i++)
{
if(check(n,i)==1)
{
a[n/9][n%9]=i;
dfs(n+1);
if(flag==1)
return 0;
a[n/9][n%9]=0;//回溯
}
}
}
//    cout<<"?"<<endl;
}
int main()
{
int t;
cin>>t;
while(t--)
{
flag=0;
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
scanf("%1d",&a[i][j]);  //一个贼有意思的输入
dfs(0);
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
printf("%d",a[i][j]);
puts("");
}
}
return 0;
}
  • 点赞
  • 收藏
  • 分享
  • 文章举报
〆℡小短腿走快点ゝ 发布了30 篇原创文章 · 获赞 1 · 访问量 1417 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: