搜索小专题

本文详细介绍了使用深度优先搜索(DFS)和广度优先搜索(BFS)算法解决经典问题的方法,包括搜索、路径遍历、砝码问题、刺杀大使任务和油滴拓展等。通过代码实例展示了如何在实际场景中应用这两种基本的图遍历算法。
摘要由CSDN通过智能技术生成

很水很水的一道搜索题
分别用dfs和bfs写
在这里插入图片描述
bfs
其实更容易理解一点
push旧节点拓展出来的新节点,pop旧节点,直到队列为空。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =110;
int a[N][N];
bool m[N][N];
int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
queue<pair<int, int>> q;
pair<int,int>p;
void bfs(int x,int y)
{
	m[x][y]=1;
	p.first = x;
	p.second = y;
	q.push(p);
	while(!q.empty())
	{
        int nx = p.first;
        int ny = p.second;
        q.pop();
		for(int i=0;i<=3;i++)
		{
			if(!a[dx[i]+nx][dy[i]+ny]&&!m[dx[i]+nx][dy[i]+ny])
			{
                p.first = dx[i] + nx;
                p.second = dy[i] + ny;
                m[p.first][p.second]=1;
                q.push(p);
            }
        }
	}
}
int main()
{
  	int n;
  	cin>>n;
  	for(int i=0;i<=n+1;i++)
  	{
  		for(int j=0;j<=n+1;j++)
  		{
  			a[i][j]=1;
  			m[i][j]=1;
  		}
  	}
  	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cin>>a[i][j];
			if(a[i][j]==1)
				m[i][j]=1;
			else m[i][j]=0;
			//cout<<m[i][j]<<' ';
		}
	}
	int nx,ny;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if((i==1||j==1||i==n||j==n)&&a[i][j]==0)
			{
				//cout<<i<<' '<<j<<endl;
				bfs(i,j);
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			//cout<<m[i][j]<<' ';
			if(m[i][j]==0)
				cout<<2<<' ';
			else cout<<a[i][j]<<' ';
		}
		cout<<endl;
	}
  	return 0;
}

dfs

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =110;
int a[N][N];
bool m[N][N];
int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
void dfs(int x,int y)
{
	if(m[x][y]==1)
		return;
	m[x][y]=1;
	for(int i=0;i<=3;i++)
	{
		int nx=x+dx[i];
		int ny=y+dy[i];
		if(a[nx][ny]==0&&m[nx][ny]==0)
			dfs(nx,ny);
	}
}
int main()
{
  	int n;
  	cin>>n;
  	for(int i=0;i<=n+1;i++)
  	{
  		for(int j=0;j<=n+1;j++)
  		{
  			a[i][j]=1;
  			m[i][j]=1;
  		}
  	}
  	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cin>>a[i][j];
			if(a[i][j]==1)
				m[i][j]=1;
			else m[i][j]=0;
			//cout<<m[i][j]<<' ';
		}
	}
	int nx,ny;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if((i==1||j==1||i==n||j==n)&&a[i][j]==0)
			{
				//cout<<i<<' '<<j<<endl;
				dfs(i,j);
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			//cout<<m[i][j]<<' ';
			if(m[i][j]==0)
				cout<<2<<' ';
			else cout<<a[i][j]<<' ';
		}
		cout<<endl;
	}
  	return 0;
}

砝码问题:

在这里插入图片描述
两个剪枝
1.从质量大的砝码开始遍历,避免了很多不必要的递归。
2.维护一个前缀和,当当前选的砝码之前的砝码和小于容量,直接全拿,跳出递归。
P5194 [USACO05DEC]Scales S

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll maxn=0;
ll a[1010];
ll sum[1010];
ll c;
bool t[1010];
void dfs(ll w,int p)
{
	if(w>c||p==0)
		return;
	maxn=max(maxn,w);
	if(sum[p-1]+w<c)
	{
		maxn=max(maxn,sum[p-1]+w);
		return ;
	}
	for(int i=1;i<p;i++)
	{
		dfs(w+a[i],i);
	}
}

int main()
{
	ll n;
	cin>>n>>c;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		sum[i]+=sum[i-1]+a[i];
	}
	dfs(0,n+1);
	cout<<maxn<<endl;
}

刺杀大使
洛谷链接
在这里插入图片描述
二分加dfs
枚举所有路径,记录伤害值,如果超过mid就往大搜,反之往小搜。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e3+10;
int mp[N][N];
bool vis[N][N];
int n,m;
int f;
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
void dfs(int xx,int yy,int mid)
{
	if(xx==n) 
	{
		f=1;
		return;		
	}
	
	for(int i=0;i<=3;i++)
	{
		int x=xx+dx[i];int y=yy+dy[i];
		if(x<1||x>n||y<1||y>m||vis[x][y]||mp[x][y]>mid)
			continue;
		vis[xx][yy]=1;
		dfs(x,y,mid);
		if(f)	
			return;
	}
}

int main()
{
	cin>>n>>m;
	int maxn=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>mp[i][j];
			maxn=max(maxn,mp[i][j]);
		}
	}
	int l=0,r=maxn;
	while(l<r)
	{
		int mid=(l+r)/2;
		memset(vis,0,sizeof(vis));
		f=0;
		dfs(1,1,mid);
		if(f)
			r=mid;
		else l=mid+1;
	}
	cout<<l<<endl;
}

油滴拓展
p
在这里插入图片描述
N最大为6,一眼全排列,有很多小细节需要注意:
1.当前计算当前油滴的半径时,要查一下是否已经落在之前拓展过的油滴内部,如果有的话,按照题意,应该直接跳过。
2.遍历油滴时,一定只遍历已经滴过的油滴。

#include <bits/stdc++.h>
#define LL long long
#define mem(i, j) memset(i, j, sizeof(i))
#define gcd(i, j) __gcd(i, j)
using namespace std;
const double pi=3.141592653589793238462643383;
struct node
{
	double nx,ny;
	double r;
	int num;
}a[10];
int b[15];
double dis(double x,double y,double xx,double yy)
{
	return(sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy)));
}
int cmp(node x,node y)
{
	return x.num<y.num;
}
double rd(double x)
{
	if(x-(int) x<0.50)
		return floor(x);
	else return ceil(x);
		
}
stack<int>st;
map<string,int>mp;
const int N = 1100;
int main()
{
	int n;
	cin>>n;
	int x,y,xx,yy;
	cin>>x>>y>>xx>>yy;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i].nx>>a[i].ny;
		a[i].num=i;
	}
	double sumxy=fabs(x-xx)*fabs(y-yy);
	//cout<<sumxy<<endl;
	double sum=0;
	double ans=1e9;
	do
	{
		sum=0;
		for(int i=1;i<=n;i++)
		{
			a[i].r=0;
			b[i]=a[i].num;
		}
		for(int i=1;i<=n;i++)
		{
			double min1=1e9+10;
			double min2=1e9+10;
			min1=min(min(fabs(a[b[i]].nx-x),fabs(a[b[i]].nx-xx)),min(fabs(a[b[i]].ny-y),fabs(a[b[i]].ny-yy)));
			for(int j=i-1;j>=1;j--)
			{
				if(b[i]!=b[j])
				{
					min2=min(min2,dis(a[b[i]].nx,a[b[i]].ny,a[b[j]].nx,a[b[j]].ny)-a[b[j]].r);
				}
			}
			if(min2<0)
				continue;
			a[b[i]].r=min(min1,min2);
			sum+=a[b[i]].r*a[b[i]].r*pi;
		}
		//cout<<sum<<endl;
		ans=min(ans,round(sumxy-sum));
	}
	while(next_permutation(a+1,a+n+1,cmp));
	printf("%0.lf",rd(ans));
}

图的dfs遍历和bfs遍历
查找文献
用邻接表存储,可以开vector数组,每个m[i],代表与之相连的其他点。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
vector<int> m[N];
bool vis[N];
queue<int> q;
void dfs(int now)
{
    vis[now] = 1;
    cout << now << ' ';
    for (auto i : m[now])
        if (!vis[i])
            dfs(i);
}
void bfs(int now)
{
    vis[now] = 1;
    q.push(now);
    while (!q.empty())
    {
        int temp = q.front();
        cout << temp << ' ';
        q.pop();
        for (auto i : m[temp])
        {
            if (!vis[i])
            {
                vis[i] = 1;
                q.push(i);
            }
        }
    }
}
int main()
{
    int x, y;
    cin >> x >> y;
    for (int i = 1; i <= y; i++)
    {
        int u, v;
        cin >> u >> v;
        m[u].push_back(v);
    }
    for (int i = 1; i <= x; i++)
        sort(m[i].begin(), m[i].end());
    dfs(1);
    cout << endl;
    memset(vis, 0, sizeof(vis));
    bfs(1);
}

icpc济南站k题
dfs遍历树,将经过第i个节点时走过的步数累加起来,并将回溯时经过的步数加上,除以n-1就是所求期望。
思路其实是猜的,虽然样例过了,但是在比赛的时候队友认为应该记录先走最长的那个路径,当时我也觉得很有道理,但是比赛前几天刚写过一个遍历邻接表的题,就随手写出来随机路线遍历的写法,没想到直接a掉了。
赛后仔细考虑发现走那条路其实都一样,这里插入lyf的博客:
凡佬博客
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
vector<int>a[N];
int vis[N];
int sum=0;
int ans=0;
void dfs(int now)
{
	sum++;
	ans+=sum-1;
	//cout<<now<<' ';
	vis[now]=1;
	for(auto i:a[now])
	{
		if(!vis[i])
		{
			dfs(i);
			sum++;
		}
	}
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		sum=0;ans=0;
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=120;i++)
			a[i].clear();
		int n;
		cin>>n;
		for(int i=1;i<n;i++)
		{
			int x,y;
			cin>>x>>y;
			a[x].push_back(y);
			a[y].push_back(x);
		}
		dfs(1);
		printf("%.6lf",ans*1.0/(n-1))
	}
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值