福建江夏学院-蓝桥杯训练赛-第一周 (部分)题解

原题都可以在51nod上查看,有具体的数据范围与测试数据,对题解有疑问的地方,请在“算法讨论”中参与讨论。

A - 小明爱修路

单源最短路问题:dijkstra+链式前向星+堆优化

题目的数据范围很大,使用邻接矩阵的内存浪费严重,并且遍历花费时间多,我们需要基于链式前向星(或邻接表)的dijkstra。

【算法】最短路径查找—Dijkstra算法_哔哩哔哩_bilibili

【AgOHの数据结构】你真的了解链式前向星吗?_哔哩哔哩_bilibili

坑:

  1. 题目是错的,不需要输入被洪水冲毁的道路条数
  2. 测试数据中有一例的最后一行有字符,无法正常判断,要输出0

只需要在使用dijkstra的时候根据道路是否损坏,判断是否要加上这条路的权值。 

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
typedef pair<int, int> P;
const int INF = 2147483647;
const int MAX_N = 5e5, MAX_M = 5e5;
int head[MAX_N], dis[MAX_N];
int n, m, s, cnt=1;
int x, y, z, k;
bool vis[MAX_N];
struct Edge {
	int  to, cost,broken, next;
}e[MAX_M];
priority_queue<P,vector<P>,greater<P>>q;
void add(int x, int y, int z,int k) {
	e[cnt].to = y;
	e[cnt].cost = z;
	e[cnt].broken = k;
	e[cnt].next = head[x];
	head[x] = cnt++;
}
void dijkstra(int s) {
	dis[s] = 0;
	q.push(make_pair(0, s));
	while (!q.empty()) {
		int x = q.top().second;
		q.pop();
		if (vis[x])continue;
		vis[x] = true;
		for (int i = head[x]; i; i = e[i].next) {
				if(dis[e[i].to] > dis[x] + e[i].cost*e[i].broken){
				dis[e[i].to] = dis[x] + e[i].cost*e[i].broken;
				q.push(make_pair(dis[e[i].to], e[i].to));
			}
		}
	}
}
int main() {
	cin >> n >> m;
	fill(dis, dis + n + 1, INF);
	for (int i = 1; i <= m; i++) {
		cin >> x >> y >> z>>k;
		add(x, y, z,k);
		add(y, x, z, k);
	}
	cin >> x >> y;
	dijkstra(x);
	if (dis[y] == INF)cout << '0';
	else cout << dis[y];
	return 0;
}

B - 最短路径

单源最短路模板题:dijkstra+链式前向星+堆优化

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
typedef pair<int, int>P;
const int INF = 1e9;
const int MAX_N = 5e5;
int head[MAX_N], dis[MAX_N],cnt = 1;
int n, m, u, v,w;
bool vis[MAX_N];
struct edge {
	int to ,cost, next;
}e[MAX_N];
void add(int x, int y, int z) {
	e[cnt].to = y;
	e[cnt].cost = z;
	e[cnt].next = head[x];
	head[x] = cnt++;
}
priority_queue<P, vector<P>, greater<P>>q;
void dijkstra(int s) {
	dis[s] = 0;
	q.push(make_pair(0, s));
	while (!q.empty()) {
		int x = q.top().second;
		q.pop();
		if (vis[x])continue;
		vis[x] = true;
		for (int i = head[x]; i; i = e[i].next) {
			if (dis[e[i].to] > dis[x] + e[i].cost) {
				dis[e[i].to] = dis[x] + e[i].cost;
				q.push(make_pair(dis[e[i].to], e[i].to));
			}
		}
	}
}
int main() {
	cin >> n >> m;
	fill(dis, dis + n+1, INF);
	for (int i = 1; i <= m; i++) {
		cin >> u >> v >> w;
		add(u, v, w);
		add(v, u, w);
	}
	cin >> u >> v;
	dijkstra(u);
	if (dis[v] == INF)cout<< '0';
	else cout << dis[v];
	return 0;
}

C - 祝寿

首先考虑亲戚回家的最短路,跑一遍dijkstra就行了。

而亲戚去爷爷家的最短路,建个返图跑一遍dijkstra。

反图可以把所有结点的编号+n建在原图的体系中。

依旧是dijkstra+链式前向星+堆优化,所以我就不贴全部代码了。

​
int main() {
	cin >> n >> m>>s;
	fill(dis, dis + 2*n+2, INF);
	for (int i = 1; i <= m; i++) {
		cin >> u >> v >> w;
		add(u, v, w);
		add(v + n, u + n, w);
	}
	dijkstra(s);
	dijkstra(s + n);
	for (int i = 1; i <= n; i++) {
		if (dis[i] == INF||dis[i+n]==INF)cout << "impossible"<<endl;
		else cout << dis[i]+dis[i + n] << endl;
	}
	return 0;
}

​

D- 快递员寄件

dijkstra,最短路乘2就是答案。

int main() {
	cin >> n >> m;
	fill(dis, dis + n+1, INF);
	for (int i = 1; i <= m; i++) {
		cin >> u >> v >> w;
		add(u, v, w);
		add(v, u, w);
	}
	dijkstra(1);
	for (int i = 2; i <= n; i++)ans += dis[i]*2;
	cout << ans;
	return 0;
}

F- 生产口罩

拓扑排序+DFS

#include<iostream>
using namespace std;
const int N =2e5+5;
int n, m,x,y,cnt=1,ans;
int head[N],in[N], out[N],ti[N],dp[N];
struct edge {
	int to, ti, next;
}e[N];
void dfs(int s) {
	for (int i = head[s]; i; i = e[i].next) {
		int v = e[i].to;
		dp[v] = max(dp[v], dp[s] + ti[v]);
		dfs(v);
	}
}
void add(int x, int y) {
	e[cnt].to = y; 
	e[cnt].ti = x;
	e[cnt].next = head[x];
	head[x] = cnt++; 
}
int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) 
		cin >> ti[i];
	for (int i = 1; i <= m; i++) {
		cin >> x >> y;
		add(x, y);
		++out[x], ++in[y];
	}
	for (int i = 1; i <= n; i++)
		if (!in[i]) {
			dp[i] = ti[i];
			dfs(i);
		}
	for (int i = 1; i <= n; i++) 
		if(!out[i])
		    ans = max(ans, dp[i]);
	cout << ans << endl;
	return 0;
}

G- 门前大桥下有很多池塘

DFS

从任意的W开始,不停地把邻接的部分用'.'代替。1次DFS后与初始的这个w连接的所有w就都被替换成了'.',因此直到图中不再存在W为止,总共进行DFS的次数就是答案了。

这种输入特别多的建议用txt文本测试。

#include<iostream>
//#include<fstream>
using namespace std;
const int N = 500;
char field[N][N];
int n,m,ans;
bool edge(int nx, int ny) {
	if (nx<1 || nx>n)return false;
	if (ny<1 || ny>m)return false;
	return true;
}
void dfs(int x,int y) {
	field[x][y] = '.';
	for(int dx=-1;dx<=1;dx++)
		for (int dy = -1; dy <= 1; dy++) {
			int nx = x + dx, ny = dy + y;
			if (edge(nx, ny) && field[nx][ny] == 'W')
				dfs(nx, ny);
		}
}
int main() {
	//ifstream myfile("data1.txt");
	cin >> n >> m;
	for (int i = 1; i <= n; i++) 
		for (int j = 1; j <= m; j++)
			cin >> field[i][j];
	for (int i = 1; i <= n; i++) 
		for (int j = 1; j <= m; j++)
			if (field[i][j] == 'W') {
				dfs(i,j);
				ans++;
			}
	cout << ans << endl;
	return 0;
}

I - 喜羊羊修高铁

并查集模板题

#include<iostream>
//#include<fstream>
using namespace std;
typedef long long LL;
const int N = 1e5;
int s[N], r[N];
int x, y;
bool f;
void init(int n) {
	for (int i = 0; i <= n; i++) {
		s[i] = i;
		r[i] = 1;
	}
}
int find(int x) {
	if (s[x] == x)return x;
	else return s[x] = find(s[x]);
}
void  unite(int x, int y) {
	x = find(x), y = find(y);
	if (x == y) {
		f = true;
		return;
	}
	if (r[x] < r[y]) s[x] = y;
	else {
		s[y] = x;
		if (r[x] == r[y])r[x]++;
	}
}
int main() {
	std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	//ifstream myfile("data1.txt");
	int t; cin >> t;
	while (t--) {
		int n, m;
		f = false;
		cin>> n >> m;
		init(n);
		for (int i = 0; i < m; i++) {
			cin >> x >> y;
			unite(x, y);
		}
		if (f)cout << "YES" << endl;
		else cout << "NO" << endl;
	}
	return 0;
}

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值