「Day 3—深度优先搜索 & 广度优先搜索」

深度优先搜索

定义

简单来说就是,一条路走到死,不行的话就回溯,继续一条路走到死,直到抵达目标点。

习题

P2052 [NOI2011] 道路修建

思路

首先,看题目对于花费的定义,道路的长度*道路两端国家数的差值的绝对值,观察一下这个应该怎么计算,我们很明显能想到树子树大小,于是我们只要知道其中一个的子树大小 \(size\),那么另一个便是 \(n-size\),于是乎,花费\(=d\times |size-(n-size)| = dx|2\times size-n|\),那么用链式前向星存一下各个国家的关系,再 \(dfs\) 去统计花费。

代码
#include<iostream>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;

#define int long long

struct node{
    int to,next,len;
}e[2 * 1000005];
int h[1000005];
int tot = 0;
inline void adge(int x,int y,int len){
    e[++ tot] = (node){y,h[x],len};
    h[x] = tot;
    return;
}

int n,m,ans=0,maxx=-1;
int size[1000005];

void dfs(int x,int f){
	size[x] = 1;
	for(int i = h[x];i != -1;i = e[i].next){
		int v = e[i].to;
		if(v != f){
			dfs(v,x);
			size[x] += size[v];
			ans += e[i].len * abs(n - 2 * size[v]);
		}
	}
}

signed main(){
	memset(h,-1,sizeof(h));
	cin >> n;
	for(int i = 1;i < n;i ++){
		int u,v,w;
		cin >> u >> v >> w;
		adge(u,v,w);
		adge(v,u,w);
	} 
	dfs(1,0);
	cout << ans << "\n";
	return 0;
}

P1434 [SHOI2002] 滑雪

思路

这个题说实话挺水的,就正常 \(dfs\),加上一个条件,即当前必须大于下一个,然后用数组记录最大的。但是但是但是,这样会超时 \(4\) 个点,为什么呢,原因是我们做了 \(n * n\)\(dfs\),而有些点搜了不止一次,于是我们就想到了记忆化。

代码
#include<iostream>
using namespace std;

int n,m;
int mp[105][105];
int vis[105][105];
int dxy[4][2]={{-1,0},{0,-1},{1,0},{0,1}};

int dfs(int x,int y){
	if(vis[x][y]) return vis[x][y];
	vis[x][y] = 1;
	for(int i = 0;i < 4;i ++){
		int dx = x + dxy[i][0];
		int dy = y + dxy[i][1];
		if(dx > 0 && dy > 0 && dx <= n && dy <= m && mp[x][y] > mp[dx][dy]){
			dfs(dx,dy);
			vis[x][y] = max(vis[x][y],vis[dx][dy] + 1);
		} 
	}
	return vis[x][y];
}

int ans = 0;

int main(){
	
	cin >> n >> m;
	for(int i = 1;i <= n;i ++){
		for(int j = 1;j <= m;j ++){
			cin >> mp[i][j];
		}
	}
	
	for(int i = 1;i <= n;i ++){
		for(int j = 1;j <= n;j ++){
			ans = max(ans,dfs(i,j));
		}
	}
	
	cout << ans << "\n";
	return 0;
}

P2853 [USACO06DEC] Cow Picnic S

思路

首先,这个题看起来很简单,就把所有牧场全 \(dfs\) 一次,如果这个点能到含有奶牛的牧场==k时,这个点就没什么问题,\(ans++\) 即可,但是但是但是牧场有\(<=1e4\)个,边有\(<=1e5\) 条,所以这样跑肯定有问题,于是我们想到反过来,既然不从点去找牛,就让牛去找牧场,以每个牛所在的位置开始 \(dfs\),如果一个点被经过了 \(k\) 次,那么这个点一定就是所以奶牛可到达的地方。理论成立,代码如下:

代码
#include<iostream>
#include<cstring>
using namespace std;

int k,n,m;
int x[100005]; 
struct node{
    int to,next;
}e[2 * 100005];
int h[100005];
bool vis[100005];
int cnt[100005];
int tot = 0;
inline void adge(int x,int y){
    e[++ tot] = (node){y,h[x]};
    h[x] = tot;
    return;
}

void dfs(int x){
	cnt[x] ++;
	for(int i = h[x];i != -1;i = e[i].next){
		int v = e[i].to;
		if(!vis[v]){
			vis[v] = 1;
			dfs(v);
		}
	}
	return;
}

int main(){
	memset(h,-1,sizeof(h));
	cin >> k >> n >> m;
	
	for(int i = 1;i <= k;i ++){
		cin >> x[i];
	}
	
	for(int i = 1;i <= m;i ++){
		int u,v;
		cin >> u >> v;
		adge(u,v);
	}
	for(int i = 1;i <= k;i ++){
		vis[x[i]] = 1;
		dfs(x[i]);
		memset(vis,0,sizeof(vis));
	}
	int ans = 0;
	for(int i = 1;i <= n;i ++){
		if(cnt[i] == k){
			ans ++;
		}
	}
	cout << ans << "\n";
	return 0;
}

P8604 [蓝桥杯 2013 国 C] 危险系数

思路

因为我们要求其关键点个数,所以呢我们可以“人性”一点,每次封锁一个点,跑一遍 \(dfs\),如果不能到终点,那么这个点就是关键点。

代码
#include<vector>
#include<iostream>
#include<cstring>
using namespace std;

vector<int> G[1005];
int n,m,ans = 0;
int fx,fy;
bool v1[1005]={0,0};
bool r[1005]={0,0};

void dfs(int x){
	if(v1[x]) return;
	if(r[x]) return;
	v1[x] = 1;
	for(auto v:G[x]){
		dfs(v);
	}
	return;
}

int main(){
	cin >> n >> m;
	for(int i = 1;i <= m;i ++){
		int u,v;
		cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	cin >> fx >> fy;
	dfs(fx);
	if(v1[fy] == 0){
		cout << "-1\n";
		return 0;
	}
	for(int i = 1;i <= n;i ++){
		if(i == fx || i == fy){
			continue;
		}
		memset(v1,0,sizeof(v1));
		r[i] = 1;
		dfs(fx);
		r[i] = 0;
		if(v1[fy] == 0){
			ans ++;
		}
	}
	cout << ans <<"\n";
	return 0;
}

P1294 高手去散步

思路

首先,用链式前向星存一下信息,为了使他们达到最长的相伴距离,那么就是找一条路,使其边权和最大,如何操作呢,我们可以从 \(n\) 个点各跑一遍 \(dfs\),在跑的过程中记录最大的,然后输出即可。

代码
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;

struct node{
    int to,next,len;
}e[10005];
int h[10005];
int vis[10005];
int tot = 0;
inline void adge(int x,int y,int len){
    e[++ tot] = (node){y,h[x],len};
    h[x] = tot;
    return;
}

int n,m,ans=0,maxx=-1;

void dfs(int x){
	for(int i = h[x];i != -1;i = e[i].next){
		int v = e[i].to;
		int w = e[i].len;
		if(vis[v] == 0){
			vis[v] = 1;
			ans += w;
			maxx = max(maxx,ans);
			dfs(v);
			ans -= w;
			vis[v] = 0;
		}
	}
}

int main(){
	memset(h,-1,sizeof(h));
	cin >> n >> m;
	for(int i = 1;i <= m;i ++){
		int u,v,w;
		cin >> u >> v >> w;
		adge(u,v,w);
		adge(v,u,w);
	} 
	for(int i = 1;i <= n;i ++){
		vis[i] = 1;
		dfs(i);
		vis[i] = 0;
	}
	cout << maxx << "\n";
	return 0;
}

P6207 [USACO06OCT] Cows on Skates G

思路

这个题吧,求 \((1,1)\)\((r,c)\)的一条任意路径,我们选择 \(dfs\),顺便记录路径。

代码
#include<iostream>
#include<cstdio>
using namespace std;

int n,m;
char mp[150][150];
int pathx[100005];
bool vis[150][150];
int pathy[100005];
int dxy[4][2] = {{1,0},{0,-1},{-1,0},{0,1}};

void dfs(int x,int y,int id){
	pathx[id] = x;
	pathy[id] = y;
	if(x == n && y == m){
		for(int i = 1;i <= id;i ++){
			cout << pathx[i] << " " << pathy[i] << "\n"; 
		}
	}
	for(int i = 0;i < 4;i ++){
		int dx = x + dxy[i][0];
		int dy = y + dxy[i][1];
		if(dx < 1 || dx > n || dy < 1 || dy > m || mp[dx][dy] == '*' || vis[dx][dy]){
			continue;
		}
		vis[dx][dy] = 1;
		dfs(dx,dy,id+1);
	}
}

int main(){
	cin >> n >> m;
	for(int i = 1;i <= n;i ++){
		scanf("%s",mp[i] + 1);
	}
	vis[1][1] = 1;
	dfs(1,1,1);
	return 0;
}

广度优先搜索

定义

别名“宽度优先搜索”,每次向下搜,一层一层搜。

习题

P1330 封锁阳光大学

思路

简单看下,发现无从下手,手动推一下,发现这是个染色问题,于是就 \(bfs\) +染色,每次选最小的。

代码
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;

struct node{
    int to,next;
}e[500005];
int h[500005],color[500005],cnt[5];
int vis[500005];
int tot = 0;
inline void adge(int x,int y){
    e[++ tot] = (node){y,h[x]};
    h[x] = tot;
    return;
}

int n,m,maxx=-0;

bool bfs(int x){
	queue<int> q;
	q.push(x);
	color[x] = 1;
	cnt[1] = 1,cnt[2] = 0; 
	while(!q.empty()){
		int v = q.front();
		q.pop();
		for(int i = h[v];i != -1;i = e[i].next){
			int t = e[i].to;
			if(color[t] == color[v]){
				return 1;
			}
			if(color[t] == 0){
				color[t] = color[v] % 2 + 1;
				cnt[color[t]] ++;
				q.push(t);
			}
		}
	}
	return 0;
}

int main(){
	memset(h,-1,sizeof(h));
	cin >> n >> m;
	for(int i = 1;i <= m;i ++){
		int u,v;
		cin >> u >> v;
		adge(u,v);
		adge(v,u);
	} 
	for(int i = 1;i <= n;i ++){
		if(color[i]) continue;
		if(bfs(i)){
			cout << "Impossible" << "\n";
			return 0;
		}
		maxx += min(cnt[1],cnt[2]);
	}
	cout << maxx << "\n";
	return 0;
}
posted @ 2024-08-06 21:48  To_Carpe_Diem  阅读(14)  评论(2编辑  收藏  举报