目录
拓扑排序 / 家谱树
p1359租用潜艇
P1938[USACO09NOV] Job Hunt S
最短路计数
797. 所有可能的路径
200.岛屿数量
DFS
BFS
695.岛屿的最大面积
DFS
BFS
P1119 灾后重建
P1629 邮递员送信
法一:Floyd
法二:Dijkstra
P3379 【模板】最近公共祖先(LCA)
最小生成树
法一:prim算法
法二:Kruskal算法
P1546 [USACO3.1] 最短网络 Agri-Net
P5764 [CQOI2005] 新年好
P2820 局域网
拓扑排序 / 家谱树
题目链接:B3644 【模板】拓扑排序 / 家谱树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
A - 拓扑排序 / 家谱树
https://www.luogu.com.cn/problem/B3644
vector<int>edeg[101];//每个点的链,存了该点连接到的所有点,便于删除其出去的边
int n, deg[101] = { 0 };//入度
void init()//建图
{
cin >> n;
int i, val;
for (i = 1; i <= n; i++)
{
while (cin >> val && val != 0)
{
edeg[i].push_back(val);
deg[val]++;
}
}
}
void toposort()//拓扑排序
{
int i;
queue<int> que;//存入度已经为0的点,方便删除出去的边
for (i = 1; i <= n; i++)
{
if (deg[i] == 0)
{
cout << i << ' ';
que.push(i);
}
}
while (!que.empty())
{
int t = que.front();
que.pop();
for (int i : edeg[t])
{
deg[i]--;
if (deg[i] == 0)
{
cout << i << ' ';
que.push(i);
}
}
}
}
int main()
{
init();//建图
toposort();//拓扑排序
return 0;
}
p1359租用潜艇
Floyd的用法,同时涉及了dp
P1359 租用游艇
https://www.luogu.com.cn/problem/P1359
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <deque>
#include <functional>
#include <climits>
#define quickio ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define endl "\n"
using namespace std;
typedef long long ll;
//半矩阵:三角矩阵
const int N = 200 + 5;
int board[N][N];
int main()
{
quickio;
int n;
cin >> n;
int i, j;
int dp[N] = { 0 };//dp[i]:存的是从i点到n点的最短距离
for (i = 1; i < n; i++)
{
for (j = i + 1; j <= n; j++)
{
cin >> board[i][j];
}
dp[i] = 1e6;
}
//注意要倒着跑
for (i = n - 1; i >= 1; i--)
{
for (j = i + 1; j <= n; j++)
{
dp[i] = min(dp[i], dp[j] + board[i][j]);
}
}
cout << dp[1] << endl;
return 0;
}
P1938[USACO09NOV] Job Hunt S
题目链接:[USACO09NOV] Job Hunt S - 洛谷
题目思路:将工资加在路径的权值中,求的是最长路径---->工资最多
P1938[USACO09NOV] Job Hunt S
// //https://www.luogu.com.cn/problem/P1938
//单源最长路径
//注意Dij不可以用于负边的情况
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <unordered_map>#include <unordered_set>#include <map>#include <set>#include <queue>#include <stack>#include <deque>#include <functional>#include <climits>
#define quickio ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);#define endl "\n"
using namespace std;typedef long long ll;
const int N = 220 + 5;const int M = 150 + 350 + 5;
int head[N];//链的个数int cnt;int vis[N];int dis[N];int countx[N];
int D, P, C, F, S;
struct EDGE
{
int v;
int next;
int w;
}EDGE[M];//
void add(int u, int v, int w){
cnt++;
EDGE[cnt].v = v;
EDGE[cnt].w = w;
EDGE[cnt].next = head[u];
head[u] = cnt;
}
//该题不同点:求的是最长路径
void SPFA(){
queue<int>que;//存点
que.push(S);
memset(dis, 0, sizeof(dis));
dis[S] = D;
vis[S] = 1;
while (!que.empty())
{
int dian = que.front();
que.pop();
vis[dian] = 0;/**/
countx[dian]++;
if (countx[dian] == C)
{
cout << -1 << endl;
exit(0);
}
for (int j = head[dian]; j; j = EDGE[j].next)
{
if (dis[dian] + EDGE[j].w > dis[EDGE[j].v])
{
dis[EDGE[j].v] = dis[dian] + EDGE[j].w;
if (vis[EDGE[j].v] == 0)
{
que.push(EDGE[j].v);
vis[EDGE[j].v] = 1;
}
}
}
}
}
int main(){
quickio;
cin >> D >> P >> C >> F >> S;
int i, u, v, w;
for (i = 0; i < P; i++)
{
cin >> u >> v;
add(u, v, D);/**/
}
for (i = 0; i < F; i++)
{
cin >> u >> v >> w;
add(u, v, D - w);/**/
}
SPFA();
int maxx = INT_MIN;
for (i = 1; i <= C; i++)
{
maxx = max(maxx, dis[i]);
}
cout << maxx << endl;
return 0;
}
最短路计数
题目思路:多用了一个countx数组计数,也注意mod的运用
最短路计数
//最短路计数
//https://www.luogu.com.cn/problem/P1144
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <deque>
#include <functional>
#include <climits>
#define quickio ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define endl "\n"
using namespace std;typedef long long ll;
const int N = 1e6 + 5;const int M = 2e6 + 5;
int head[N*2];//链的个数
int cnt;int vis[N*2];
int ans[N*2];
int countx[N*2];
struct EDGE
{
int to;
int next;
int w;
}edge[M*2];//
void add(int u, int v, int w){
cnt++;
edge[cnt].to = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
void Dij(){
priority_queue<pair<int, int> >que;
que.push({0, 1});
ans[1] = 0;
countx[1] = 1;
while (!que.empty())
{
int dis = que.top().first;
int dian = que.top().second;
que.pop();
if (vis[dian])
continue;
vis[dian] = 1;
for (int j = head[dian]; j; j = edge[j].next)
{
if (ans[dian] + edge[j].w < ans[edge[j].to])
{
ans[edge[j].to] = ans[dian] + edge[j].w;
countx[edge[j].to] = countx[dian];
que.push({-ans[edge[j].to], edge[j].to});
}
else if (ans[dian] + edge[j].w == ans[edge[j].to])
{
countx[edge[j].to] = (countx[edge[j].to] %100003 + countx[dian]%100003)%100003;
countx[edge[j].to] %= 100003;
}
}
}
}
int main(){
quickio;
int n, m;
cin >> n >> m;
int i;
int u, v;
for (i = 1; i <= m; i++)
{
cin >> u >> v;
add(u, v, 1);
add(v, u, 1);//注意是无向边
}
memset(ans, 0x3f, sizeof(ans));
Dij();
for (i = 1; i <= n; i++)
cout << countx[i] << endl;
return 0;
}
797. 所有可能的路径
Dfs模板:
Dfs,和深搜一样的模板
void dfs(参数)
{
if (终止条件)
{
处理结果;
return;
}
for(该层元素)
{
处理节点;
DFS;
处理结果
}
}
题目链接: . - 力扣(LeetCode)
797. 所有可能的路径
//https://leetcode.cn/problems/all-paths-from-source-to-target/
//图:一般一维数组存路径,二维数组存所有结果
class Solution {
public:
vector<vector<int> >res;
vector<int> path;
void dfs(vector<vector<int>>& graph, int cur/*当前所在的点*/)
{
if (cur == graph.size() - 1)
{
res.push_back(path);
return;
}
for (int i = 0; i < graph[cur].size(); i++)
{
path.push_back(graph[cur][i]);
dfs(graph, graph[cur][i]);
path.pop_back();
}
}
vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph)
{
path.push_back(0);
dfs(graph, 0);
return res;
}
};
200.岛屿数量
知识点:
BFS,和Dij的优先队列类似
DFS和BFS都要用到to数组(存四个走向)
满足条件的加入队列或者dfs继续深搜
要用到vis数组,标记已经遍历过的点
题目链接:. - 力扣(LeetCode)
DFS
200. 岛屿数量
//https://leetcode.cn/problems/number-of-islands/
//题意:只要周边都是水,就算一个岛屿
//只有四个方向可以走
//深搜:用一个计数器,记录连接在一起的陆地数量,当不是陆地时并且遍历完所属区域时,返回
class Solution {
public:
int to[4][2] = {{0, 1}, {1, 0},{-1, 0}, {0, -1}};
int vis[305][305] = { 0 };
int count = 0;
void dfs(vector<vector<char>>& grid, int x, int y)
{
//没有终止条件的原因:下面的循环中就对继续深搜设置了条件,不满足条件的不继续搜
for (int i = 0; i < 4; i++)
{
int nextx = x + to[i][0];
int nexty = y + to[i][1];
if (nextx < 0 || nexty < 0 || nextx >= grid.size() || nexty >= grid[0].size())
continue;
if (grid[nextx][nexty] == '1' && vis[nextx][nexty] == 0)
{
vis[nextx][nexty] = 1;
dfs(grid, nextx, nexty);
}
}
}
int numIslands(vector<vector<char>>& grid)
{
for (int i = 0; i < grid.size(); i++)
{
for (int j = 0; j < grid[i].size(); j++)
{
if (vis[i][j] == 0 && grid[i][j] == '1')
{
count++;
vis[i][j] = 1;
dfs(grid, i, j);
}
}
}
return count;
}
};
BFS
//广搜:用队列,有模板:最短路径模板
class Solution {
public:
int to[4][2] = {{0, 1}, {1, 0},{-1, 0}, {0, -1}};
int vis[305][305] = { 0 };
int count = 0;
void bfs(vector<vector<char>>& grid)
{
queue<pair<int, int> >que;
for (int p = 0; p < grid.size(); p++)
{
for (int q = 0; q < grid[p].size(); q++)
{
if (vis[p][q] == 0 && grid[p][q] == '1')
{
count++;
que.push({p, q});
while (!que.empty())
{
int x = que.front().first;
int y = que.front().second;
que.pop();
vis[x][y] = 1;
for (int i = 0; i < 4; i++)
{
int nextx = x + to[i][0];
int nexty = y + to[i][1];
if (nextx < 0 || nexty < 0 || nextx >= grid.size() || nexty >= grid[0].size())
continue;
if (vis[nextx][nexty] == 0 && grid[nextx][nexty] == '1')
{
que.push({nextx, nexty});
vis[nextx][nexty] = 1;
}
}
}
}
}
}
}
int numIslands(vector<vector<char>>& grid)
{
bfs(grid);
return count;
}
};
695.岛屿的最大面积
题目链接:. - 力扣(LeetCode)
DFS
695. 岛屿的最大面积
//https://leetcode.cn/problems/max-area-of-island/
//仍然两种图遍历方法都能写,dfs:层数计数,bfs:队列大小计数
//dfs
class Solution {
public:
int count = 0;
int vis[55][55] = { 0 };/*必需*/
int to[4][2] = { 0, 1, 1, 0, -1, 0, 0, -1 };/*必需*/
int temp;
void dfs(vector<vector<int>>& grid, int x, int y)
{
//也是在后面就把回溯条件给确定了,不满足的就不会来递归
if (temp > count)
count = temp;
for (int i = 0; i < 4; i++)
{
int nextx = x + to[i][0];
int nexty = y + to[i][1];
if (nextx < 0 || nexty < 0 || nextx >= grid.size() || nexty >= grid[0].size())
continue;
if (vis[nextx][nexty] == 0 && grid[nextx][nexty] == 1)
{
vis[nextx][nexty] = 1;
temp++;
dfs(grid, nextx, nexty);
}
}
}
int maxAreaOfIsland(vector<vector<int>>& grid)
{
for (int i = 0; i < grid.size(); i++)
{
for (int j = 0; j < grid[i].size(); j++)
{
if (vis[i][j] == 0 && grid[i][j] == 1)
{
vis[i][j] = 1;
temp = 1;/*注意是1*/
dfs(grid, i, j);
}
}
}
return count;
}
};
BFS
//BFS
class Solution {
public:
int count = 0;
int vis[55][55] = { 0 };/*必需*/
int to[4][2] = { 0, 1, 1, 0, -1, 0, 0, -1 };/*必需*/
int temp;
void BFS(vector<vector<int>>& grid)
{
queue<pair<int, int> > que;
int temp;
for (int i = 0; i < grid.size(); i++)
{
for (int j = 0; j < grid[i].size(); j++)
{
if (vis[i][j] == 0 && grid[i][j] == 1)
{
que.push({i, j});
temp = 1;
while (!que.empty())
{
int x = que.front().first;
int y = que.front().second;
que.pop();
vis[x][y] = 1;
for (int p = 0; p < 4; p++)
{
int nextx = x + to[p][0];
int nexty = y + to[p][1];
if (nextx < 0 || nexty < 0 || nextx >= grid.size() || nexty >= grid[0].size())
continue;
if (vis[nextx][nexty] == 0 && grid[nextx][nexty] == 1)
{
vis[nextx][nexty] = 1;
temp++;
que.push({nextx, nexty});
}
}
}
count = max(count, temp);
}
}
}
}
int maxAreaOfIsland(vector<vector<int>>& grid)
{
BFS(grid);
return count;
}
};
P1119 灾后重建
题目链接:灾后重建 - 洛谷
P1119 灾后重建
注意z是一直增大的,所以可以用一个cnt再每次询问时更新并且同时更新board里存的最短路径,
因为一开始board初始化为INT_MAX,所以注意两个board元素加一起的时候用int存储会导致答案错误,因此要board设置为ll
//https://www.luogu.com.cn/problem/P1119
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <deque>
#include <functional>
#include <climits>
#define quickio ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define endl "\n"
using namespace std;
typedef long long ll;
const int N = 205;
const int M = 20005;
ll board[N][N];/*注意数组必须开ll,否则加起来超过int,就答案与原来不符*/
int t[N];
int n, m;
void Floyd(int k)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (board[i][k] == INT_MAX || board[k][j] == INT_MAX)
continue;
if (board[i][k] + board[k][j] < board[i][j])
board[i][j] = board[j][i] = board[i][k] + board[k][j];
}
}
}
int main()
{
quickio;
cin >> n >> m;
int i;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
board[i][j] = INT_MAX;
}
}
for (i = 0; i < n; i++)
{
cin >> t[i];
board[i][i] = 0;
}
int u, v, w;
while (m--)
{
cin >> u >> v >> w;
board[u][v] = board[v][u] = w;
}
int q;
cin >> q;
int x, y, z;
int cnt = 0;
while (q--)
{
cin >> x >> y >> z;
while (t[cnt] <= z && cnt < n)
{
Floyd(cnt);
cnt++;
}
if (z < t[x] || z < t[y])
{
cout << -1 << endl;
}
else
{
if (board[x][y] != INT_MAX)
cout << board[x][y] << endl;
else
cout << -1 << endl;
}
}
return 0;
}
P1629 邮递员送信
题目链接:邮递员送信 - 洛谷
法一:Floyd
超时
P1629 邮递员送信
建反图,注意要将各个数组开为平时写的两倍,否则存不下
//https://www.luogu.com.cn/problem/P1629
/*法一:Floyd*/
//两个样例超时
//#include <climits>
//#include <iostream>
//
//using namespace std;
//
//const int MAX = 1010;
//int board[MAX][MAX];
//
//int main()
//{
// int n, m;
// cin >> n >> m;
//
// int i, j;
// for (i = 1; i <= n; i++)
// {
// for (j = 1; j <= n; j++)
// {
// if (i == j)
// board[i][j] = 0;
// else
// board[i][j] = INT_MAX;
// }
// }
//
// int u, v, w;
// for (i = 1; i <= m; i++)
// {
// cin >> u >> v >> w;
// if (w < board[u][v])
// board[u][v] = w;
// }
//
// int k;
// for (k = 1; k <= n; k++)
// {
// for (i = 1; i <= n; i++)
// {
// if (i == k)
// continue;
// for (j = 1; j <= n; j++)
// {
// if (j == k)
// continue;
//
// if (i == j)
// continue;
//
// if (board[i][k] != INT_MAX && board[k][j] != INT_MAX)
// {
// if (board[i][k] + board[k][j] < board[i][j])
// board[i][j] = board[i][k] + board[k][j];
// }
// }
// }
// }
//
// int sum = 0;
// for (i = 2; i <= n; i++)
// {
// sum += board[i][1] + board[1][i];
// }
// cout << sum << endl;
// return 0;
//}
法二:Dijkstra
/*法二:dijkstra*/
//反向建图
//按题中给的示例:1 -> 2 -> 3 -> 5 -> 4 ->1,建返图后就是 1 -> 2 + 6 -> 9 -> 10 -> 8
/*注意MAX的值设定,因为最大权值可以到达1e4,然后边数可以到达1e5,所以至少要大于1e9*/
/*!!!!!涉及链式前向星!!!!!!!!*/
/*
链式前向星
*/
/*注意各个数组大小的设定*/
#include <iostream>
#include <queue>
#include <climits>
#include <stdlib.h>
#include <string.h>
using namespace std;
const int MAXM = 100005;
const int MAXN = 1005;
int head[MAXN * 2];//每个点的链
int cnt;
int ans[MAXN * 2];//起点 到 该点最短路径
int vis[MAXN * 2];//是否该点被加入进最短路径中
struct EDGE
{
int to;
int next;
int wei;
}edge[MAXM * 2];/*每条边的数据*/
void add(int u, int v, int w)//创建边 的函数
{
cnt++;
edge[cnt].to = v;
edge[cnt].wei = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
void dij(int s)//求 s 到 各个点 的最短路径
{
priority_queue<pair<int, int> >que;//距离, 点号
que.push({ 0, s });
while (!que.empty())
{
int h = que.top().first;//距离
int ph = que.top().second;//点
que.pop();
if (vis[ph] == 0)
{
vis[ph] = 1;
for (int i = head[ph]; i != 0; i = edge[i].next)
{
if (ans[edge[i].to] > ans[ph] + edge[i].wei)
{
ans[edge[i].to] = ans[ph] + edge[i].wei;
if (vis[edge[i].to] == 0)
{
que.push({ - ans[edge[i].to], edge[i].to});
}
}
}
}
}
}
int main()
{
int n, m;
cin >> n >> m;
int i;
for (i = 1; i <= n; i++)
{
ans[i] = INT_MAX;
}
ans[1] = 0;
int u, v, w;
for (i = 1; i <= m; i++)
{
cin >> u >> v >> w;
add(u, v, w);
add(v + n, u + n, w);//建反边!!!!!!!!!!重点
}
dij(1);
int sum = 0;
for (i = 2; i <= n; i++)
sum += ans[i];
memset(vis, 0, sizeof vis);
for (i = 1 + n; i <= n + n; i++)
{
ans[i] = INT_MAX;
}
ans[1 + n] = 0;
dij(1 + n);
for (i = 2 + n; i <= n + n; i++)
sum += ans[i];
cout << sum << endl;
return 0;
}
P3379 【模板】最近公共祖先(LCA)
题目链接:【模板】最近公共祖先(LCA) - 洛谷
//P3379 【模板】最近公共祖先(LCA)
//https://www.luogu.com.cn/problem/P3379
//倍增法求最近公共祖先
//1.朴素(先跳到同意深度,再都往上找,第一次相遇的点就算最近公共祖先)//2.LAC(找2的次方个父亲) //3.Tarjan(用了并查集)
//(先将询问放到相应节点的叶子节点下,当该枝遍历完后
//,找询问是否已经被并再某个并查集,若并了,
//则所在并查集就是其公共祖先)
//每过一个节点就放入并查集一次,再继续递归
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <deque>
#include <functional>
#include <climits>
#define quickio ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);#define endl "\n"
using namespace std;
typedef long long ll;
const int N = 5e5 + 5;
const int M = 5e5 + 5;
int head[N * 2];
int cnt;
int n, m, s;
int f[N][30];//f[i][j]:i点的第2^j个父亲,注意是30,不是20
int dep[N];//深度
struct EDGE
{
int v;
int next;
}EDGE[M * 2];
void add(int u, int v){
cnt++;
EDGE[cnt].v = v;
EDGE[cnt].next = head[u];
head[u] = cnt;
}
void dfs(int v, int father)//孩子,父亲{
dep[v] = dep[father] + 1;//孩子深度= 父亲深度 + 1
f[v][0] = father;//父亲一定是孩子的第1个父亲,也就是2^0个
for (int i = 1/*注意从1开始*/; (1 << i)/*2的i次方*/ <= dep[v]; i++)
{
f[v][i] = f[f[v][i - 1]][i - 1];/*公式*/
}
//注意把v的孩子的情况也要处理
for (int j = head[v]; j; j = EDGE[j].next)
{
if (EDGE[j].v != father)/*!!!!!!!!!!!!!!!!!!!!*/
dfs(EDGE[j].v, v);
}
}
int LCA(int x, int y){
if (dep[x] < dep[y])//注意x要比y下面
swap(x, y);
//先将x, y调到同一层
for (int i = 20; i >= 0; i--)
{
if (dep[f[x][i]] >= dep[y])
//如果x的第i个父亲的深度 > y的深度,就更新x
x = f[x][i];
}
if (x == y)
//说明x, y中有一点就算公共祖先
return x;
//移到公共祖先的下一层
for (int i = 20; i >= 0; i--)
{
if (f[x][i] != f[y][i])
//注意两者不能重合,最多只能移动到公共祖先的下一层
{
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];//上一层就是公共祖先
}
int main(){
quickio;
cin >> n >> m >> s;
int i;
int x, y;
for (i = 0; i < n - 1; i++)
{
cin >> x >> y;
add(x, y);
add(y, x);/*注意是双边,不仅注意要反向添加
还要把数组大小初始化两倍*/
}
dfs(s,0);//根节点,0(因为没有0节点,所以0节点表示根节点父亲)
int a, b;
while (m--)
{
cin >> a >> b;
cout << LCA(a, b) << endl;
}
return 0;
}
最小生成树
题目链接:【模板】最小生成树 - 洛谷
法一:prim算法
P3366 【模板】最小生成树
//https://www.luogu.com.cn/problem/P3366
prim算法
// 注意都是无向边
//要用到d[N]:存某点到圈外点的最短距离,vis[N]:存某点是否出圈,出圈了就标记为1
//ans:记录最小生成树的值
//countx:记录加入生成树的点数,只要该点的d[]不等于inf,count就++
//初始化:一开始除了起点都初始化为inf
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <deque>
#include <functional>
#include <climits>
#define quickio ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define endl "\n"
using namespace std;
typedef long long ll;
const int N = 5005;
const int M = 2e5 + 5;
int head[N * 2];//注意是无向边
int cnt;
int vis[N];
int d[N];
int ans;//记录最小生成树的各边长度之和
int countx;//记录加入最小生成树的节点个数
int n, m;
struct EDGE
{
int v;
int w;
int next;
}EDGE[M * 2];
void add(int u, int v, int w)
{
cnt++;
EDGE[cnt].v = v;
EDGE[cnt].w = w;
EDGE[cnt].next = head[u];
head[u] = cnt;
}
bool prim(int s)
{
priority_queue<pair<int, int> >que;
que.push({ 0, s });
memset(d, 0x3f, sizeof(d));
d[s] = 0;
while (!que.empty())
{
int dis = -que.top().first;
int dian = que.top().second;
que.pop();
if (dis != d[dian])
continue;
if (vis[dian] == 0)
{
vis[dian] = 1;
if (d[dian] != 0x3f3f3f3f)
countx++;
ans += d[dian];
for (int j = head[dian]; j; j = EDGE[j].next)
{
int t = EDGE[j].v;
int w = EDGE[j].w;
if (vis[t] == 0 && d[t] > w)
{
d[t] = w;
que.push({ -d[t], t });
}
}
}
}
return countx == n;
}
int main()
{
quickio;
cin >> n >> m;
int u, v, w;
for (int i = 1; i <= m; i++)
{
cin >> u >> v >> w;
add(u, v, w);
add(v, u, w);
}
prim(1);
if (countx == n)
cout << ans << endl;
else
cout << "orz" << endl;
return 0;
}
法二:Kruskal算法
法二:Kruskal算法
//用到了并查集,并且排序了所有边----->贪心思想----->最小边就有可能构成最小生成树
//步骤:
//初始化并查集(每个节点的父节点都是自己)
//按边权从小到大排序(用到sort和重载<)
//按顺序枚举每一条边,如果这条边连接的两个点不在同一集合,就将其加入最小生成树,并且合并两棵树
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <deque>
#include <functional>
#include <climits>
#define quickio ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define endl "\n"
using namespace std;
typedef long long ll;
const int M = 2e5 + 5;
const int N = 5005;
//int head[N];
//int d[N];
int fa[N];
int cnt;
int ans;//存最小生成树答案
int countx;//存边数
int n, m;
struct EDGE
{
int u;/*注意,多了个u*/
int v;
int w;
//int next;
bool operator<(const EDGE& t)
{
return w < t.w;
}
}EDGE[M * 2];//注意是无向边
int find(int x)
{
if (fa[x] == x)
return x;
return fa[x] = find(fa[x]);
}
void add(int u, int v, int w)
{
cnt++;
EDGE[cnt].v = v;
EDGE[cnt].w = w;
//EDGE[cnt].next = head[u];
EDGE[cnt].u = u;
//head[u] = cnt;
}
bool Kruskal()
{
sort(EDGE + 1, EDGE + 1+ m);
for (int i = 1; i <= m; i++)
{
int x = find(EDGE[i].u);
int y = find(EDGE[i].v);
if (x != y)
{
fa[x] = y;/*注意是等于y,不是等于fa[y]*/
ans += EDGE[i].w;
countx++;
}
}
return countx == n - 1;
}
int main()
{
quickio;
cin >> n >> m;
int u, v, w;
for (int i = 0; i< m; i++)
{
cin >> u >> v >> w;
add(u, v, w);
//add(v, u, w);/*!!!!!!!!注意,不要建双向边!!!!!!!!*/
}
for (int i = 1; i <= n; i++)
fa[i] = i;
bool res = Kruskal();
if (res)
{
cout << ans << endl;
}
else
cout << "orz" << endl;
return 0;
}
P1546 [USACO3.1] 最短网络 Agri-Net
题目链接:[USACO3.1] 最短网络 Agri-Net - 洛谷
P1546 [USACO3.1] 最短网络 Agri-Net
//https://www.luogu.com.cn/problem/P1546
//和最小生成树一样的思路p----->prim算法
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <deque>
#include <functional>
#include <climits>
#define quickio ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define endl "\n"
using namespace std;
typedef long long ll;
const int N = 105;
const int M = 10000000;
//int board[N][N];
int d[N];
int ans;
int cnt;
int countx;
int head[N];
int vis[N];
int n;
struct EDGE
{
int v;
int w;
int next;
}EDGE[M * 2];
void add(int u, int v, int w)
{
cnt++;
EDGE[cnt].next = head[u];
EDGE[cnt].v = v;
EDGE[cnt].w = w;
head[u] = cnt;
}
void prim(int s)
{
priority_queue<pair<int, int> >que;
que.push({ 0, s });
memset(d, 0x3f, sizeof(d));
d[s] = 0;
while (!que.empty())
{
int dis = -que.top().first;
int dian = que.top().second;
que.pop();
if (dis != d[dian])
continue;
if (d[dian] != 0x3f3f3f3f)
countx++;
ans += d[dian];
if (vis[dian] == 0)
{
vis[dian] = 1;
for (int j = head[dian]; j; j = EDGE[j].next)
{
int t = EDGE[j].v;
int w = EDGE[j].w;
if (vis[t] == 0 && d[t] > w)
{
d[t] = w;
que.push({ -d[t], t });
}
}
}
}
}
int main()
{
quickio;
cin >> n;
int w;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
//cin >> board[i][j];
cin >> w;
add(i, j, w);
add(j, i, w);
}
}
prim(1);
cout << ans << endl;
return 0;
}
P5764 [CQOI2005] 新年好
题目链接:[CQOI2005] 新年好 - 洛谷文章来源:https://www.toymoban.com/news/detail-851390.html
P5764 [CQOI2005] 新年好
//https://www.luogu.com.cn/problem/P5764
//求的是图里面的最短路径,而不是树里面的了
//要找的是最短顺序
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <deque>
#include <functional>
#include <climits>
#define quickio ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define endl "\n"
using namespace std;
typedef long long ll;
const int N = 50005;
const int M = 2e5 + 5;
int head[N];//注意是无向边
int cnt;
int vis[N];//不是Dij里,而是dfs里的
int d[7][N];//存的是第i个亲戚到各点的最短距离
int rel[7];/*亲戚*/
int ans = 0x3f3f3f3f;
int n, m;
struct EDGE
{
int v;
int w;
int next;
}EDGE[M * 2];
void add(int u, int v, int w)
{
cnt++;
EDGE[cnt].v = v;
EDGE[cnt].w = w;
EDGE[cnt].next = head[u];
head[u] = cnt;
}
void Dij(int s)
{
priority_queue<pair<int, int> >que;
que.push({ 0, rel[s] });
d[s][rel[s]] = 0;
while (!que.empty())
{
int dis = -que.top().first;
int dian = que.top().second;
que.pop();
if (dis != d[s][dian])
continue;
for (int j = head[dian]; j; j = EDGE[j].next)
{
int t = EDGE[j].v;
int w = EDGE[j].w;
if (d[s][t] > w + d[s][dian])
{
d[s][t] = w + d[s][dian];
que.push({ -d[s][t], t });
}
}
}
}
void dfs(int cur/*当前遍历亲戚个数*/, int cost/*到该点的距离*/, int pos/*当前亲戚*/)
{
if (cost > ans)
return;
if (cur == 5)
{
ans = min(ans, cost);
return;
}
for (int i = 1; i < 6/*是小于,不是等于,因为一开始就遍历了从家出发*/; i++)
{
if (vis[i] == 0)
{
vis[i] = 1;
dfs(cur + 1/*遍历了的亲戚个数+1*/, cost + d[pos][rel[i]]/*加上当前亲戚到第i个亲戚的最短距离*/, i/*递归处理第i个亲戚*/);
vis[i] = 0;
}
}
}
int main()
{
quickio;
cin >> n >> m;
for (int i = 1; i <= 5; i++)
cin >>rel[i];
rel[6] = 1;
int u, v, w;
for (int i = 1; i <= m; i++)
{
cin >> u >> v >> w;
add(u, v, w);
add(v, u, w);
}
memset(d, 0x3f, sizeof(d));
for (int i = 1; i <= 6; i++)
Dij(i);
memset(vis, 0, sizeof(vis));
dfs(0, 0, 6);
cout << ans << endl;
return 0;
}
P2820 局域网
题目链接:局域网 - 洛谷文章来源地址https://www.toymoban.com/news/detail-851390.html
P2820 局域网
//https://www.luogu.com.cn/problem/P2820
//就是求最短路的变形
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <deque>
#include <functional>
#include <climits>
#define quickio ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define endl "\n"
using namespace std;
typedef long long ll;
int n, k;
const int N = 105;
const int M = 10000000;
int d[N];
int ans;
int cnt;
int countx;
int head[N];
int vis[N];
int sum;
struct EDGE
{
int v;
int w;
int next;
}EDGE[M * 2];
void add(int u, int v, int w)
{
cnt++;
EDGE[cnt].next = head[u];
EDGE[cnt].v = v;
EDGE[cnt].w = w;
head[u] = cnt;
}
void prim(int s)
{
priority_queue<pair<int, int> >que;
que.push({ 0, s });
memset(d, 0x3f, sizeof(d));
d[s] = 0;
while (!que.empty())
{
int dis = -que.top().first;
int dian = que.top().second;
que.pop();
if (dis != d[dian])
continue;
if (d[dian] != 0x3f3f3f3f)
countx++;
ans += d[dian];
if (vis[dian] == 0)
{
vis[dian] = 1;
for (int j = head[dian]; j; j = EDGE[j].next)
{
int t = EDGE[j].v;
int w = EDGE[j].w;
if (vis[t] == 0 && d[t] > w)
{
d[t] = w;
que.push({ -d[t], t });
}
}
}
}
}
int main()
{
cin >> n >> k;
int u, v, w;
int s;
int minx = INT_MAX;
while (k--)
{
cin >> u >> v >> w;
add(u, v, w);
add(v, u, w);//注意也是无向
sum += w;
if (w != 0 && w < minx)
{
minx = w;
s = u;/**/
}
}
prim(s);/*不一定是1,而是取有最短路径的点*/
cout << sum - ans << endl;
return 0;
}
到了这里,关于《图》经典题题解(拓扑排序,DFS,BFS,Floyd,Dijkstra,LCA最近公共祖先,最小生成树,最短路径)(ACM)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!