原题都可以在51nod上查看,有具体的数据范围与测试数据,对题解有疑问的地方,请在“算法讨论”中参与讨论。
A - 小明爱修路
单源最短路问题:dijkstra+链式前向星+堆优化
题目的数据范围很大,使用邻接矩阵的内存浪费严重,并且遍历花费时间多,我们需要基于链式前向星(或邻接表)的dijkstra。
【算法】最短路径查找—Dijkstra算法_哔哩哔哩_bilibili
【AgOHの数据结构】你真的了解链式前向星吗?_哔哩哔哩_bilibili
坑:
- 题目是错的,不需要输入被洪水冲毁的道路条数
- 测试数据中有一例的最后一行有字符,无法正常判断,要输出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;
}