文章目录
A. DS图应用–最短路径(含代码框架)
题目描述
给出一个图的邻接矩阵,再给出指定顶点v0,求顶点v0到其他顶点的最短路径
代码框架如下:
输入
第一行输入t,表示有t个测试实例
第二行输入n,表示第1个图有n个结点
第三行起,每行输入邻接矩阵的一行,以此类推输入n行
第i个结点与其他结点如果相连则为1,无连接则为0,数据之间用空格隔开
第四行输入v0,表示求v0到其他顶点的最短路径距离
以此类推输入下一个示例
输出
每行输出v0到某个顶点的最短距离和最短路径
每行格式:v0编号-其他顶点编号----[最短路径],具体请参考示范数据
输入样例1
1
5
0 5 0 7 15
0 0 5 0 0
0 0 0 0 1
0 0 2 0 0
0 0 0 0 0
0
输出
0-1-5----[0 1 ]
0-2-9----[0 3 2 ]
0-3-7----[0 3 ]
0-4-10----[0 3 2 4 ]
代码
#include <bits/stdc++.h>
using namespace std;
const int MaxLen = 20;
const int MaxDist = 9999;
class Map {
private:
int Matrix[MaxLen][MaxLen];
int Vexnum;
public:
void SetMatrix(int vnum, int mx[MaxLen][MaxLen]);
void ShortestPath_DIJ(int v0);
};
void Map::SetMatrix(int vnum, int mx[MaxLen][MaxLen]) {
int i, j;
Vexnum = vnum;
for (i = 0; i < Vexnum; i++)
for (j = 0; j < Vexnum; j++)
Matrix[i][j] = MaxDist;
for (i = 0; i < Vexnum; i++)
for (j = 0; j < Vexnum; j++)
if (mx[i][j]) Matrix[i][j] = mx[i][j];
}
void Map::ShortestPath_DIJ(int v0) {
int i, j, v, w, min;
int *dist = new int[Vexnum];
bool *final = new bool[Vexnum];
int path[MaxLen][MaxLen];
int len[MaxLen];
for (j = 0; j < Vexnum; j++) {
final[j] = false;
dist[j] = Matrix[v0][j];
}
for (i = 0; i < Vexnum; i++)
for (j = 0; j < Vexnum; j++)
path[i][j] = -1;
for (i = 0; i < Vexnum; i++) {
if (dist[i] < MaxDist) {
path[i][1] = i;
path[i][0] = v0;
}
len[i] = 2;
}
dist[v0] = 0;
final[v0] = true;
for (i = 0; i < Vexnum; i++) {
min = MaxDist;
for (w = 0; w < Vexnum; w++)
if (!final[w])
if (dist[w] < min) {
v = w;
min = dist[w];
}
final[v] = true;
for (w = 0; w < Vexnum; w++) {
if (!final[w] && (min + Matrix[v][w] < dist[w])) {
len[w] = 0;
dist[w] = min + Matrix[v][w];
for (j = 0; path[v][j] != -1; j++) {
path[w][j] = path[v][j];
len[w]++;
}
path[w][j] = w;
len[w]++;
}
}
}
for (i = 0; i < Vexnum; i++) {
if (i != v0) {
cout << v0 << '-' << i << '-' << dist[i] << "----" << '[';
for (j = 0; j < len[i]; j++)
cout << path[i][j] << ' ';
cout << ']' << endl;
}
}
delete[]dist;
delete[]final;
}
int main() {
int i, j, k, t;
int vnum, v0;
int mx[MaxLen][MaxLen];
Map myMap;
cin >> t;
while (t--) {
for (i = 0; i < MaxLen; i++)
for (j = 0; j < MaxLen; j++)
mx[i][j] = 0;
cin >> vnum;
for (i = 0; i < vnum; i++)
for (j = 0; j < vnum; j++)
cin >> mx[i][j];
myMap.SetMatrix(vnum, mx);
cin >> v0;
myMap.ShortestPath_DIJ(v0);
}
return 0;
}
B. 拓扑排序-STL版
题目描述
已知有向图,顶点从0开始编号,求它的求拓扑有序序列。
拓扑排序算法:给出有向图邻接矩阵
1.逐列扫描矩阵,找出入度为0且编号最小的顶点v
2.输出v,并标识v已访问
3.把矩阵第v行全清0
重复上述步骤,直到所有顶点输出为止
/
// STL::vector基本用法
1)
vector::push_back(1); //将1增加到数组尾部
vector::push_back(e); //将类型为E的一个对象e增加到数组尾部.其中类型E可以自行定义,如Vertex类,int类等。
2.1)
vector vec;
int x = vec[1]; //读取组数v(索引从0开始)的第1个元素
2.2)
vector v;
int& x = v[1]; //引用组数v的第1个元素,注意此处的引用&的用法
x = 10; //修改组数v的第1个元素为10
3)
vector<vector > adjMat; //声明一个矩阵对象adjMat
vector row; //声明一个vector(动态数组)对象row表示矩阵的一行
int j=0;
for(; j<n; ++j) {
int edge;
cin >> edge;
row.push_back(edge); //将边信息增加到数组row的最后
}
this->adjMat.push_back(row); //将row增加到数组adjMat的最后
//
//参考代码框架
#include
#include
using namespace std;
class Graph {
public:
vector isFinished; //索引号所指示的顶点是否已处理过
vector<vector > adjMat; //邻接矩阵
int n; //顶点数 as 成员变量
public:
void readAdjMatrix() {
//从输入读入邻接矩阵,存入this->adjMat
cin >> this->n; //顶点数
int i=0;
for(; i<n; ++i) {
//TODO:设置this->isFinished数组:每个顶点未曾访问过
//提示:调用vector::push_back方法
vector row;
int j=0;
for(; j<n; ++j) {
int edge;
cin >> edge; //读入顶点对i,j之间是否有一条边
//TODO:将边信息增加入row
}
//TODO:将row增加入this->adjMat
//提示:以row为参数,调用adjMat的push_back方法
}
}
bool isZeroInDegrees(int vertexNo) {
//判定顶点vertexNo是否没有入度
int i=0;
//this->adjMat[i][vertexNo] == 0
//表示顶点i与vertexNo之间没有边相连
for(; i<n && this->adjMat[i][vertexNo] == 0; ++i);
//TODO:返回true if 顶点vertexNo没有入度; false [otherwise]
}
int select() {
//从所有顶点中,选出一个顶点i,满足:
//1) i没有处理过,即isFinished[i]=false
//2) i的入度为0
int i = 0;
for (; i < n; ++i) {
//TODO:若顶点i的已经处理过,则pass
//TODO:若顶点度>0,则pass
//提示:调用isZeroInDegrees
//TODO: 设置顶点i已经处理过,即isFinished[i]为正确值
//TODO: 返回i
}
//TODO: 返回-1, 表示未找到满足1)+2)条件的顶点
}
void update(int rootNo) {
//更新顶点rootNo的出弧,即将顶点rootNo从图中断开
int j=0;
for(;j<n; ++j) {
//TODO: 设置adjMat[rootNo][j]为0
}
}
/
// 拓扑排序主算法
void topologySort() {
int i=0;
for(; i<n; ++i) { //遍历n次:即按拓扑序,依次选择(排出)n个顶点
int root; // 声明顶点索引号(编号)用于存放本次选出的顶点
//TODO: 调用select方法,并将其返回值存入root
//TODO: 若root=-1,则break;
// root=-1表示没有可以排出的顶点
//TODO: 以root为参数,调用update方法
//TODO:输出当前选出的顶点root 和一个空格
}
//TODO:输出一行
}
void main() {
readAdjMatrix();
topologySort();
}
};
int main() {
int t;
cin >> t;
while (t–) {
Graph g;
g.main();
}
return 0;
}
输入
第一行输入一个整数t,表示有t个有向图
第二行输入n,表示图有n个顶点
第三行起,输入n行整数,表示图对应的邻接矩阵
以此类推输入下一个图的顶点数和邻接矩阵
输出
每行输出一个图的拓扑有序序列
输入样例1
2
5
0 1 0 1 1
0 0 1 0 0
0 0 0 0 1
0 0 1 0 0
0 0 0 0 0
7
0 0 0 0 0 0 0
1 0 1 1 0 0 0
1 0 0 0 0 0 0
1 0 1 0 0 0 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 0 0 1 0 1 0
输出
0 1 3 2 4
4 6 5 1 3 2 0
代码
#include <iostream>
#include <vector>
using namespace std;
class Graph {
public:
vector<bool> isFinished; //索引号所指示的顶点是否已处理过
vector<vector<int> > adjMat; //邻接矩阵
int n; //顶点数 as 成员变量
public:
void readAdjMatrix() {
//从输入读入邻接矩阵,存入this->adjMat
cin >> this->n; //顶点数
int i, j;
for (i = 0; i < n; ++i) {
for (int k = 0; k < n; k++)
isFinished.push_back(false);
//TODO:设置this->isFinished数组:每个顶点未曾访问过
//提示:调用vector::push_back方法
vector<int> row;
for (j = 0; j < n; ++j) {
int edge;
cin >> edge; //读入顶点对i,j之间是否有一条边
row.push_back(edge);
//TODO:将边信息增加入row
}
this->adjMat.push_back(row);
//TODO:将row增加入this->adjMat
//提示:以row为参数,调用adjMat的push_back方法
}
}
bool isZeroInDegrees(int vertexNo) {
//判定顶点vertexNo是否没有入度
int i;
//this->adjMat[i][vertexNo] == 0
//表示顶点i与vertexNo之间没有边相连
for (i = 0; i < n && this->adjMat[i][vertexNo] == 0; ++i){}
if(i>=n) return true;
else return false;
//TODO:返回true if 顶点vertexNo没有入度; false [otherwise]
}
int select() {
//从所有顶点中,选出一个顶点i,满足:
//1) i没有处理过,即isFinished[i]=false
//2) i的入度为0
int i = 0;
for (; i < n; ++i) {
if(isFinished[i]) continue;
//TODO:若顶点i的已经处理过,则pass
if(!isZeroInDegrees(i)) continue;
//TODO:若顶点度>0,则pass
//提示:调用isZeroInDegrees
isFinished[i]=true;
//TODO: 设置顶点i已经处理过,即isFinished[i]为正确值
return i;
//TODO: 返回i
}
return -1;
//TODO: 返回-1, 表示未找到满足1)+2)条件的顶点
}
void update(int rootNo) {
//更新顶点rootNo的出弧,即将顶点rootNo从图中断开
int j = 0;
for (; j < n; ++j) {
adjMat[rootNo][j]=0;
//TODO: 设置adjMat[rootNo][j]为0
}
}
/
// 拓扑排序主算法
void topologySort() {
int i = 0;
for (; i < n; ++i) { //遍历n次:即按拓扑序,依次选择(排出)n个顶点
int root; // 声明顶点索引号(编号)用于存放本次选出的顶点
root=select();
//TODO: 调用select方法,并将其返回值存入root
if(root==-1) break;
//TODO: 若root=-1,则break;
// root=-1表示没有可以排出的顶点
update(root);
//TODO: 以root为参数,调用update方法
cout<<root<<' ';
//TODO:输出当前选出的顶点root 和一个空格
}
cout<<endl;
//TODO:输出一行
}
void main() {
readAdjMatrix();
topologySort();
}
};
int main() {
int t;
cin >> t;
while (t--) {
Graph g;
g.main();
}
return 0;
}
C. 关键路径-STL版
题目描述
给定有向图无环的边信息,求每个顶点的最早开始时间、最迟开始时间。
// 参考代码
#include
#include
#include
#include
using namespace std;
class Vertex {
public:
int indexNo;
bool hasEnterQueue;
int early;
int later;
Vertex(int indexNo) {
this->indexNo = indexNo;
this->hasEnterQueue = false;
early = -1;
later = 0x7FFFF;
}
void updateEarly(int parentEarly, int edgeValue) {
int newEarly = parentEarly + edgeValue;
if (newEarly > this->early)
this->early = newEarly;
}
void updateLater(int childLater, int edgeValue) {
int newLater = childLater - edgeValue;
if (newLater < this->later)
this->later = newLater;
}
};
class Graph {
public:
vector vertexes;
vector<vector > adjMat;
int n;
public:
void readVertexes() {
//TODO: 将顶点数读入成员变量n
//TODO: 从输入初始化vertexes数组
int i=0;
for(; i<n; ++i) {
Vertex v(i);
this->vertexes.push_back(v);
}
//为成员变量adjMat创建内存,赋初值
for(i=0; i<n; ++i) {
vector<int> row;
int j=0;
for(; j<n; ++j) {
//TODO: 将0增加到row最后
}
//TODO: 将row增加到adjMat最后
}
}
void readAdjMatrix() {
//read the adjacent info into this->adjMat
int edges;
cin >> edges;
int i=0;
int s, t, w; //s源顶点编号,t目的顶点编号,w边长
for(; i<edges; ++i) {
//TODO: 读入s,t,w,并将adjMat的第s行、第t列的值改为w.
}
}
void updateEarly(int parentNo, queue<int>& earlyQue) {
int parentEarly = vertexes[parentNo].early; //读入父结点early值
int j=0;
for(; j<n; ++j) {
int edgeValue = adjMat[parentNo][j];
if (edgeValue == 0) continue; //若父结点与结点j没有边相连,pass
Vertex& child = vertexes[j];
child.updateEarly(parentEarly, edgeValue); //更新子结点j的early信息
if(!child.hasEnterQueue) {
child.hasEnterQueue = true; //将子结点加入队列
earlyQue.push(j);
}
}
}
void updateLater(int childNo, queue<int>& laterQue) {
//TODO:
}
int getRoot() {
//获取入度为0的顶点
int j=0;
for(; j<n; ++j) {
int i=0;
for(; i<n && adjMat[i][j] == 0; ++i);
if (i>=n) return j; //j has not any in-edges.
}
return -1; //表示没找到
}
int getLeaf() {
//TODO: 获取出度为0的顶点
}
void printEarlyLater(bool isEarly) {
int i=0;
for(; i<n; ++i) {
Vertex& v = vertexes[i];
if (isEarly)
cout << v.early << " ";
else {
cout << v.later << " ";
}
}
cout << endl;
}
void findEarly() {
//执行关键路径算法,求每个顶点的最早开始时间。
int r = getRoot();
Vertex& root = vertexes[r];
root.hasEnterQueue = true;
root.early = 0;
queue<int> que;
que.push(r);
while(!que.empty()) {
int p = que.front();
que.pop();
updateEarly(p, que);
}
printEarlyLater(true);
}
void clearEnterQueue() {
int i=0;
for(; i<n; ++i) {
vertexes[i].hasEnterQueue = false;
}
}
void findLater() {
//TODO:调用clearEnterQueue,以清除每个顶点的hasEnterQueue=false
//执行关键路径算法,求每个顶点的最迟开始时间。
}
void main() {
readVertexes();
readAdjMatrix();
findEarly();
findLater();
}
};
int main() {
int t=1;
//cin >> t;
while (t–) {
Graph g;
g.main();
}
return 0;
}
输入
第一行图的顶点总数
第二行边的总数
第三行开始,每条边的时间长度,格式为源结点 目的结点 长度
输出
第一行:第个顶点的最早开始时间
第二行:每个顶点的最迟开始时间
输入样例1
9
12
0 1 3
0 2 10
1 3 9
1 4 13
2 4 12
2 5 7
3 6 8
3 7 4
4 7 6
5 7 11
6 8 2
7 8 5
输出
0 3 10 12 22 17 20 28 33
0 9 10 23 22 17 31 28 33
代码
#include <iostream>
#include <vector>
#include <string>
#include <queue>
using namespace std;
class Vertex {
public:
int indexNo;
bool hasEnterQueue;
int early;
int later;
Vertex(int indexNo) {
this->indexNo = indexNo;
this->hasEnterQueue = false;
early = -1;
later = 0x7FFFF;
}
void updateEarly(int parentEarly, int edgeValue) {
int newEarly = parentEarly + edgeValue;
if (newEarly > this->early)
this->early = newEarly;
}
void updateLater(int childLater, int edgeValue) {
int newLater = childLater - edgeValue;
if (newLater < this->later)
this->later = newLater;
}
};
class Graph {
public:
vector<Vertex> vertexes;
vector<vector<int> > adjMat;
int n;
public:
void readVertexes() {
cin >> this->n;
//TODO: 将顶点数读入成员变量n
//TODO: 从输入初始化vertexes数组
int i;
for (i = 0; i < n; ++i) {
Vertex v(i);
this->vertexes.push_back(v);
}
//为成员变量adjMat创建内存,赋初值
for (i = 0; i < n; ++i) {
vector<int> row;
int j = 0;
for (; j < n; ++j) {
row.push_back(0);
//TODO: 将0增加到row最后
}
adjMat.push_back(row);
//TODO: 将row增加到adjMat最后
}
}
void readAdjMatrix() {
//read the adjacent info into this->adjMat
int edges;
cin >> edges;
int i = 0;
int s, t, w; //s源顶点编号,t目的顶点编号,w边长
for (; i < edges; ++i) {
cin >> s >> t >> w;
adjMat[s][t] = w;
//TODO: 读入s,t,w,并将adjMat的第s行、第t列的值改为w.
}
}
void updateEarly(int parentNo, queue<int> &earlyQue) {
int parentEarly = vertexes[parentNo].early; //读入父结点early值
int j = 0;
for (; j < n; ++j) {
int edgeValue = adjMat[parentNo][j];
if (edgeValue == 0) continue; //若父结点与结点j没有边相连,pass
Vertex &child = vertexes[j];
child.updateEarly(parentEarly, edgeValue); //更新子结点j的early信息
if (!child.hasEnterQueue) {
child.hasEnterQueue = true; //将子结点加入队列
earlyQue.push(j);
}
}
}
void updateLater(int childNo, queue<int> &laterQue) {
int childlater = vertexes[childNo].later; //读入父结点early值
int j = 0;
for (; j < n; ++j) {
int edgeValue = adjMat[j][childNo];
if (edgeValue == 0) continue; //若子结点与结点j没有边相连,pass
Vertex &father = vertexes[j];
father.updateLater(childlater, edgeValue);//更新父结点j的later信息
if (!father.hasEnterQueue) {
father.hasEnterQueue = true; //将fu结点加入队列
laterQue.push(j);
}
}
//TODO:
}
int getRoot() {
//获取入度为0的顶点
int j = 0;
for (; j < n; ++j) {
int i = 0;
for (; i < n && adjMat[i][j] == 0; ++i);
if (i >= n) return j; //j has not any in-edges.
}
return -1; //表示没找到
}
int getLeaf() {
int i, j;
for (i = 0; i < n; i++) {
for (j = 0; j < n && adjMat[i][j] == 0; j++);
if (j >= n) return i;
}
return -1;
//TODO: 获取出度为0的顶点
}
void printEarlyLater(bool isEarly) {
int i = 0;
for (; i < n; ++i) {
Vertex &v = vertexes[i];
if (isEarly)
cout << v.early << " ";
else {
cout << v.later << " ";
}
}
cout << endl;
}
void findEarly() {
//执行关键路径算法,求每个顶点的最早开始时间。
int r = getRoot();
Vertex &root = vertexes[r];
root.hasEnterQueue = true;
root.early = 0;
queue<int> que;
que.push(r);
while (!que.empty()) {
int p = que.front();
que.pop();
updateEarly(p, que);
}
printEarlyLater(true);
}
void clearEnterQueue() {
int i = 0;
for (; i < n; ++i) {
vertexes[i].hasEnterQueue = false;
}
}
void findLater() {
int i;
clearEnterQueue();
int L = getLeaf();
Vertex &leaf = vertexes[L];
leaf.hasEnterQueue = true;
leaf.later = leaf.early;
queue<int> que;
que.push(L);
while (!que.empty()) {
int p = que.front();
que.pop();
updateLater(p, que);
}
printEarlyLater(false);
//TODO:调用clearEnterQueue,以清除每个顶点的hasEnterQueue=false
//TODO:执行关键路径算法,求每个顶点的最迟开始时间。
}
void main() {
readVertexes();
readAdjMatrix();
findEarly();
findLater();
}
};
int main() {
int t = 1;
while (t--) {
Graph g;
g.main();
}
return 0;
}
D. 道路建设 (Ver. I)
题目描述
有N个村庄,编号从1到N,你应该建造一些道路,使每个村庄都可以相互连接。
两个村A和B是相连的,当且仅当A和B之间有一条道路,或者存在一个村C使得在A和C之间有一条道路,并且C和B相连。
现在一些村庄之间已经有一些道路,你的任务就是修建一些道路,使所有村庄都连通起来,并且所有道路的长度总和是最小的。
输入
测试数据有多组
第一行是整数N(3 <= N <= 100),代表村庄的数量。 然后是N行,其中第i行包含N个整数,这些N个整数中的第j个是村庄i和村庄j之间的距离(距离是[1,1000]内的整数)。
然后是整数Q(0 <= Q <= N *(N + 1)/ 2),接下来是Q行,每行包含两个整数a和b(1 <= a <b <= N),代表着村庄a和村庄b之间的道路已经建成。
输出
对于每组测试数据
输出一个整数,表示要构建的道路的长度总和最小值
输入样例1
3
0 990 692
990 0 179
692 179 0
1
1 2
输出
179
代码
#include <bits/stdc++.h>
using namespace std;
const int MaxLen = 100;
const int inf = 9999;
struct closEdge{
int lowCost;
int point;
};
class Graph{
int Mat[MaxLen][MaxLen];
int n;
public:
void readGraph(int m){
this->n= m;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
cin>>Mat[i][j];
if(!Mat[i][j])
Mat[i][j] = inf;
}
}
}
void origin_road(){
int m;
cin>>m;
for (int i = 0; i < m; ++i) {
int a,b;
cin>>a>>b;
Mat[a-1][b-1] =0;
Mat[b-1][a-1] =0;
}
prime();
}
void prime(){
int start =0;
closEdge c[n];
int sum=0;
for (int i = 0; i < n; ++i) {
if(i!=start){
c[i].point = start;
c[i].lowCost = Mat[start][i];
}
}
c[start].lowCost = -1;
for (int i = 1; i < n; ++i) {
int min = inf;
for (int j = 0; j < n; ++j) {
if(c[j].lowCost==-1) {
continue;
}
if(c[j].lowCost<min){
start = j;
min = c[j].lowCost;
}
}
sum+=min;
c[start].lowCost = -1;
for (int j = 0; j < n; ++j) {
if(Mat[start][j]<c[j].lowCost){
c[j].point = start;
c[j].lowCost = Mat[start][j];
}
}
}
cout<<sum<<endl;
}
};
int main(){
int n;
while (cin>>n) {
Graph g;
g.readGraph(n);
g.origin_road();
}
return 0;
}