GDUT 20 寒假集训专题1.dfs/bfs+二分
-
总结
-
题目题解
1.dfs/bfs
dfs以“能走多远就走多远"为基本原则,是非常重要的一种搜索方法。
这里有一个在洛谷看到的dfs模板
int search(int t)
{
if(满足输出条件)
{
输出解;
}
else
{
for(int i=1;i<=尝试方法数;i++)
if(满足进一步搜索条件)
{
为进一步搜索所需要的状态打上标记;
search(t+1);
恢复到打标记前的状态;//也就是说的{回溯一步}
}
}
}
相对的bfs就先尽可能的搜索与已搜索顶点相邻的未搜索顶点,然后以此类推不断扩大搜索范围,常用来求最短路径。
下面是用图+队列来实现bfs的代码
计算每个顶点到开始顶点的最短路径
int G[105][105],color[105],d[105],n,t;//color是状态,0表示未曾进队,1表示在队列中,2表示出队列 数组d是各顶点到开始顶点的最短距离
void bfs(int u)
{
queue <int > Q;
d[u]=0;
color[u]=1;
Q.push(u);
int v;
while(!Q.empty() ){
u=Q.front() ;Q.pop() ;
color[u]=2;
for(int i=1;i<=n;i++){
if(G[u][i]==1&&color[i]==0){
Q.push(i);
d[i]=d[u]+1;
color[i]=1;
}
}
}
}
2.二分
二分的包括二分查找以及二分答案等等…
这里是二分查找的模板
int bin(int *a,int size,int p)
{
int l=0;
int r=size-1;
while(l<=r){
int mid=l+(r-l)/2;
if(p==a[mid]){
return mid;
}
else if(p>a[mid]){
l=mid+1;
}
else{
r=mid-1;
}
}
return -1;
}
如果是用c++,c++的algorithm里面有几个二分函数
具体调用如下
#include <algorithm>
int coun(int l,int r) //包含端点
{
int *s,*f,p;
s=lower_bound(mei,mei+d,l); //下界,第一个大于等于l
f=upper_bound(mei,mei+d,r);//上界,第一个大于r的元素
p=f-s;
return p;
}
善于运用这两个二分查找,其实可以用来查找一个区间的数,就是用上述代码实现的
二分答案也有模板,具体如下
int bin(int *a,int size,int p)
{
int l=0,ans;
int r=size-1;
while(l<=r){
int mid=l+(r-l)/2;
if(judge(mid)){ //这里就是写一个判断的函数,判断mid是否符合题意,是就返回1,否
ans=mid; //就返回0
l=mid+1; //是变大还是变小需要根据题目的要求来变化
}
else{
r=mid-1;
}
}
cout<<ans;
}
接下来贴两道相关的题目
1.Lake Counting
题目描述:由于最近的降雨,水在农民约翰的田地的不同地方聚集,这是一个矩形N x M (1 <= N <= 100;1 <= M <= 100)正方形。每个正方形要么包含水(‘W’),要么包含旱地(’.’)。农民约翰想算出他的田地里已经形成了多少个池塘。池塘是一组相连的有水的广场,一个广场被认为是相邻的所有8个邻居。给农民约翰的田地一张图,算出他有多少个池塘。
解题思路:这道题用dfs,bfs应该都行,我用的是dfs,开二维数组把图存下来,把旱地标为0,水地标为1,先从第一个水地开始dfs,往八个方向一直搜索,能搜索到的地方都是属于这一个池塘,然后记为1个,并且把这时候搜索到的同属第一个池塘的所有水地改成旱地,然后继续执行上一步,直到所有地方都变成旱地,答案也就出来了。
ac代码:
#include <iostream>
#include <stdio.h>
using namespace std;
int dx[8]={1,1,1,0,0,-1,-1,-1};
int dy[8]={0,1,-1,1,-1,0,1,-1};
int G[105][105],n,m,color[105][105],count;
void dfs(int x,int y)
{
color[x][y]=0;
G[x][y]=0;
for(int i=0;i<8;i++){
int nx=x+dx[i];
int ny=y+dy[i];
if(nx<0||ny<0||nx>=n||ny>=m){
continue;
}
if(G[nx][ny]==1&&color[nx][ny]==1){
dfs(nx,ny);
}
}
}
int main()
{
int i,j;
char ch;
cin>>n>>m;
getchar();
for(i=0;i<n;i++){
for(j=0;j<m;j++){
color[i][j]=1;
cin>>ch;
if(ch=='.'){
G[i][j]=0;
}
else {
G[i][j]=1;
}
}
getchar();
}
for(i=0;i<n;i++){
for(j=0;j<m;j++){
if(color[i][j]==1&&G[i][j]==1){
dfs(i,j);
count++;
}
}
}
cout<<count;
return 0;
}
2.Red and Black
题目描述:有一个长方形的房间,上面铺着方瓷砖。每个贴图都是红色或黑色。一个人站在一块黑瓷砖上。从一个贴图,他可以移动到四个相邻贴图中的一个。但是他不能在红色的贴图上移动,只能在黑色的贴图上移动。写一个程序来计算他通过重复上面描述的动作可以达到的黑色方块的数量。
解题思路:这一题我是用的bfs,同样的我用二维数组存下来整个图,把能走的黑色记为0,把不能走的红色记为1,也差不多是障碍物的意思。把开始的位置放入bfs函数里面,从这一点开始,把这一点入队,然后因为只有他一个点,所以进入循环他就出队了,出队之后把它四个方向并且能走的地方继续入队,同时统计入队个数,如此循环操作直到队列为空了,也就是没法再走了,答案也就出来了。
ac代码
#include <iostream>
#include <queue>
#include <stdio.h>
using namespace std;
struct node {
int x,y;
};
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int n,m,G[25][25],color[25][25],coun;
void bfs(int x,int y)
{
struct node xy;
xy.x=x;
xy.y=y;
coun++;
color[x][y]=0;
queue <node > Q;
Q.push(xy);
int nx,ny;
while(!Q.empty()){
xy=Q.front() ;Q.pop() ;
x=xy.x;
y=xy.y;
for(int i=0;i<4;i++){
nx=x+dx[i];
ny=y+dy[i];
if(nx<0||ny<0||nx>=n||ny>=m){
continue;
}
if(G[nx][ny]==0&&color[nx][ny]==1){
color[nx][ny]=0;
coun++;
xy.x=nx;
xy.y=ny;
Q.push(xy);
}
}
}
}
int main()
{
int i,j,x,y;
char ch;
cin>>m>>n;
getchar();
while(n!=0){
for(i=0;i<n;i++){
for(j=0;j<m;j++){
color[i][j]=1;
cin>>ch;
if(ch=='.'){
G[i][j]=0;
}
else if(ch=='#'){
G[i][j]=1;
}
else {
x=i;
y=j;
}
}
getchar();
}
coun=0;
bfs(x,y);
cout<<coun<<endl;
cin>>m>>n;
getchar();
}
return 0;
}