《王道论坛核算机考研机试攻略》第五章【图论】_考研 算法 图论-C…(王道论坛)

并查集最小生成树(mst)
kruskal算法最短途径
floyd算法dijkstra算法拓扑排序

并查集

int findroot(int x) {
if (tree[x] -1)
return x;
else
return findroot(tree[x]);
}

int findroot(int x) {
int ret;
while (tree[x] ! -1)
x
return ret;
}

int findroot(int x) {
if (tree[x] -1)
return x;
else {
int tmp // 将其时节点的双亲节点设置为查找回来的根节点编号
return tmp;
}
}

int findroot(int x) {
int ret;
int tmp
while (tree[x] ! -1)
x // 在做一次从节点x到根节点的遍历
while (tree[x] ! -1) {
int t // 遍历进程中将这些节点的双亲节点都设置为现已查找到的根节点编号
}
return ret;
}

疏通工程

#include <iostream>
using namespace std;
const int n 1000;
int tree[n];

int findroot(int x) { // 查找某个节点地址的树的根节点
if (tree[x] -1)
return x;
else {
int tmp
return tmp;
}
}
int main() {
int n, m;
while (scanf(, &n) ! 0) {
scanf(, &m);
for (int i 1; i < -1; // 初始时其本身就是地址树根节点
while (m--) {
int a, b;
scanf(, &a, &b);
a // 查找边的两个极点地址集结信息
if (a ! b) // 若两个极点不在同一个集结则兼并这两个集结
tree[a]
}
int ans 0;
for (int i 1; i <) {
if (tree[i] -1)
ans // 计算一切节点中根节点的个数
}
printf(, ans-1); // 答案即为在ans个集结间再建筑ans-1条路途即可使一切节点连通
}
return 0;
}

more is better

#include <iostream>
#include <algorithm>
using namespace std;
const int n 10000001;
int tree[n];
int sum[n];
int findroot(int x) { // 查找某个节点地址的树的根节点
if (tree[x] -1)
return x;
else {
int tmp
return tmp;
}
}
int main() {
int n;
while (scanf(, &n) ! eof) {
for (int i 1; i < n; i -1;
sum[i] 1;
}

while (n--) {
int a, b;
scanf(, &a, &b);
a
if (a !
}
int ans 1;
for (int i 1; i < n; i) {
if (sum[i] > ans)
ans
}
printf(, ans);
}
return 0;
}

最小生成树(mst)

kruskal算法

    初始时多有节点归于孤立的集结依照边权递加次序遍历一切的边并将这两个极点分属的集结兼并遍历完一切边后最小生成树不存在

如上进程所示刚好可以运用并查集来完成这些操作。

仍是疏通工程

#include <iostream>
#include <algorithm>
using namespace std;
const int n 101;
int tree[n];
int findroot(int x) { // 查找某个节点地址的树的根节点
if (tree[x] -1)
return x;
else {
int tmp
return tmp;
}
}
struct edge {
int a, b; // 边两个极点的编号
int cost; // 该边的权值
bool operator < (const edge& a) const { // 重载小于号使其可以依照边权从小到大摆放
return cost < a.cost;
}
} edge[6000];
int main() {
int n;
while (scanf(, &n) ! eof) {
for (int i 1; i < n*(n-1)/2; i) {
scanf(, &edge[i].a, &edge[i].b, &edge[i].cost);
}
sort(edge1, edge1n*(n-1)/2); // 依照边权值递加摆放一切边
for (int i 1; i < -1;
}
int ans 0;
for (int i 1; i < n*(n-1)/2; i) { // 依照边权值递加次序遍历一切边
int a
int b
if (a !
}
}
printf(, ans);
}
return 0;
}

freckles

#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int n 101;
int tree[n];
int findroot(int x) { // 查找某个节点地址的树的根节点
if (tree[x] -1)
return x;
else {
int tmp
return tmp;
}
}
struct edge {
int a, b; // 边两个极点的编号
double cost; // 该边的权值
bool operator < (const edge& a) const { // 重载小于号使其可以依照边权从小到大摆放
return cost < a.cost;
}
} edge[6000];
struct point {
double x, y;
double getdistance(const point& p) {
double tmp
return sqrt(tmp);
}
} points[101];
int main() {
int n;
while (scanf(, &n) ! eof) {
for (int i 1; i < n*(n-1)/2; i) {
scanf(, &points[i].x, &points[i].y);
}
int size 0; // 笼统出边的总数
for (int i 1; i <) {
for (int j 1; j <) { // 联接两点的线段笼统成边
edge[size].a // 该边的两个极点编号
edge[size].cost // 边权值为两点之间的长度
size
}
} // 遍历一切的点对
sort(edge, edge // 依照边权值递加摆放一切边
for (int i 1; i < -1;
}
double ans 0;
for (int i 0; i < size; i) {
int a
int b
if (a !
}
}
printf(, ans);
}
return 0;
}

最短途径

floyd算法

for (int k 1; k <) {
for (int i 1; i <) {
for (int j 1; j <) {
if (ans[i][k] infinity)
continue;
// 关于第一种情况则ans[i][j]不会再是infinity
if (ans[i][j]
}
}
}

最短路

#include<iostream>
using namespace std;
int ans[101][101];
int main() {
int n, m;
while (scanf(, &n, &m) ! eof) {
if (n 0 && m 0)
break;
for (int i 1; i <) {
for (int j 1; j < -1; // 对邻接矩阵初始化咱们用-1代表无量
}
ans[i][i] 0; // 自个到自个的途径长度设为0
}
while (m--) {
int a, b, c;
scanf(, &a, &b, &c);
ans[a][b]
}
for (int k 1; k <) {
for (int i 1; i <) {
for (int j 1; j <) {
if (ans[i][k] -1 || ans[k][j] -1)
continue;
if (ans[i][j] -1 || ans[i][k]
}
}
}
printf(, ans[1][n]);
}
return 0;
}

dijkstra算法

算法流程

《王道论坛核算机考研机试攻略》第五章【图论】_考研 算法 图论-C…(王道论坛)插图

    初始化。遍历与集结k中节点直接相邻的边(u, v, c)最终将该节点参加集结k。若集结k中现已包括了一切的点否则重复进程2

#include <vector>
#include <iostream>
using namespace std;
struct e { // 邻接链表中链表元素规划体
int next; // 代表直接相邻的节点
int c; // 代表该边的权值
};

vector<e> edge[101]; // 邻接链表
bool mark[101]; // 符号该节点现已参加集结k
/*间隔向量再经过一条边抵达节点i的途径中最短的间隔
*/
int dis[101];
int main() {
int n, m;
while (scanf(, &n, &m) ! eof){
if (n 0 && m 0)
break;
for (int i 1; i < // 初始化邻接链表
while (m--) {
int a, b, c;
scanf(, &a, &b, &c);
e tmp;
tmp.c
}
for (int i 1; i <) { // 初始化
dis[i] -1; // 一切间隔为-1即不可以达
mark[i] false; // 一切节点不归于集结k
}
dis[1] 0; // 得到迩来的点为节点1长度为0
mark[1] true; // 将节点1参加集结k
int newp 1; // 集结k中新参加的点为节点1
for (int i 1; i < n; i) { // 循环n-1次依照最短途径递加的次序断定
for (int j 0; j < edge[newp].size(); j) {
int t // 该边的另一个节点
int c // 该边的长度
if (mark[t] true) // 若另一节点也归于k则跳过
continue;
// 若该店尚不可以达或许该节点从头参加的节点经过一条边抵达时比以往间隔更短
if (dis[t] -1 || dis[t] > dis[newp]
}
int min 123123123; // 最小值初始化为一个大整数为找最小值做预备
for (int j 1; j <) {
if (mark[j] true)
continue;
if (dis[j] ! -1 && dis[j] < min) {
min true;
}
// printf( // 输出答案
}
return 0
}

最短途径疑问

#include <iostream>
#include <vector>
using namespace std;

struct e {
int next;
int d;
int p;
e(int _n, int _d, int _p) : next(_n), d(_d), p(_p) {}
} ;
vector<e> edge[1005];
bool visited[1005];
int dis[1005];
int spend[1005];
int main() {
int n, m;
while (scanf(, &n, &m) ! eof) {
if (n 0 && m 0)
break;
for (int i 1; i<
int a, b, d, p;
while (m--) {
scanf(, &a, &b, &d, &p);
e tmp(b, d, p);
edge[a].push_back(tmp);
tmp.next
}
for (int i 1; i < false;
dis[i] -1;
spend[i] 9999999;
}
int s, t;
scanf(, &s, &t);
int newp true;
dis[newp] 0;
spend[newp] 0;
for (int i 1; i < n; i) {
for (int j 0; j < edge[newp].size(); j) {
int next
int d
int p
if (visited[next])
continue;
if (dis[next] -1 || dis[next] > dis[newp]
}
}
int min 9999999;
for (int i 1; i <) {
if (visited[i])
continue;
if (dis[i] ! -1 && dis[i] < min) {
min true;
}
printf(, dis[t], spend[t]);
}
return 0;
}

拓扑排序

leagal or not 在一个 qq 群里有着许多师徒联络一个学徒也可以会有许多不一样的师父。 输入给出该群里一切的师徒联络即判别该图是不是为有向无环图。 input: the input consists of several test cases. for each case, the first line contains two integers, n (members to be tested) and m (relationships to be tested)(2 < 0.to make it simple, we give every one a number (0, 1, 2,…, n-1). we use their numbers instead of their names.

样例输入
3 2
0 1
1 2
2 2
0 1
1 0
0 0
样例输出
yes
no

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
vector<int> edge[501]; // 邻接链表所以vector中的元素为int
queue<int> q; // 保存入度为0的节点的行列

int main() {
int indegree[501]; // 计算每个节点的入度
int n, m;
while (scanf(, &n, &m) ! eof) {
if (n 0 && m 0)
break;
for (int i 0; i < n; i) { // 初始化一切节点留心本题节点编号由0到n-1
indegree[i] 0; // 初始化入读信息一切节点入度均为0
edge[i].clear(); // 清空邻接链表
}
while (m--) {
int a, b;
scanf (, &a, &b); // 读入一条由a指向b的有向边
indegree[b] // 累加节点b的入度
edge[a].push_back(b); // 将b参加a的邻接链表
}
// 若行列非空
while (!q.empty())
q.pop();
for (int i 0; i < n; i) {
if (indegree[i] 0)
q.push(i); // 若节点入度为0将其放入行列
}
int cnt 0; // 计数器用于累加现已断定拓扑序列的节点个数
while (!q.empty()) {
// 读出队头节点编号则将该节点紧接着放在现已断定的拓扑次序后
int nowp
for (int i 0; i < edge[nowp].size(); i // 去掉某条边后该边所指向后继节点入度减1
if (indegree[edge[nowp][i]] 0) { // 若该节点入度变为0
q.push(edge[nowp][i]); // 将其放入行列中
}
}
}
if (cnt n)
cout << << endl;
else
cout << << endl;
}
return 0;
}

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注