蓝桥杯算法入门_13 (树、图、最短路径)

构造图

邻接矩阵

#include <iostream>
using namespace std;

/*图、最短路径 、*/
/*图和树

基本概念:
有向图
入度 出度
性质:所有的入度 == 所有的出度


无向图任何一对顶点之间都有一条边相连,  即  n* (n - 1) / 2 条不重复的边  == 完全图
度
性质: 无向图边数 == 所有顶点度数和的一半
度序列  abcde对应的度序列 ,也可以乱序  ,即图可以有多种度序列

一个序列 判断是否可以确定唯一的图的
图论算法:从第二个数开始往后数 三个数 均减一  ,再把后两个减1 ,若之后的都减为0 ,则可以 (可能bug,暂时不用)

点集 V(G)
边集 E(G)

*/

/*邻接矩阵   --数组
#include<cstring>
初始化  memset(G,0,sizeof(G));   //全部初始为0    ,memset只能初始0 或 1 ,且要加头文件cstring


输入:

10
1 2
2 1
1 4
1 5
5 1
4 5
5 4
3 4
2 4
4 1

*/

#include<cstring>

void test_01() {
	int G[6][6];
	memset(G,0,sizeof(G));
	int m;
	cin >> m;
	for(int i = 0; i < m ; i++) {
		int a,b;
		cin >> a>> b;
		G[a][b] = 1;
	}

	for(int i = 1; i <= 5; i++) {
		int sum = 0;
		for(int j = 1; j <= 5; j++) {
			if(G[i][j] == 1 && G[j][i] == 1) { //互相连接
				sum++;
			}
		}
		cout<< i << "有" <<sum << "个真正的朋友" <<endl;
	}
	return;
}

邻接表

/*邻接表   -- 链表  或 -- 动态数组 vector    注意:由于vector常数较大,较严格的限时,可能有超时风险  (满分需链表)
稀疏图时,存储数据优化空间
有向图:
每个点出发有几条边

10
1 2
2 4
3 4
5 6
7 9
10 1
8 9
9 5
3 7
3 8


*/
#include<vector>
void test_02() {
	vector<int> G[11];   //动态数组  -- 二维
	int m;
	cin >>m;
	for(int i = 0; i < m; i++) {
		int a,b;
		cin >> a >> b;
		G[a].push_back(b);  //添加边
		G[b].push_back(a);
	}
	for(int i = 1; i <= 10; i++) {
		cout << i <<" : ";
		for(int j = 0; j < G[i].size(); j++) {  //打印每个顶点的邻接
			cout <<G[i][j] <<" ";
		}
		cout << endl;
	}
	return;
}

/*
带权的边 能用邻接表存

输入:
10
1 2 3
2 4 4
3 4 5
5 6 1
7 9 0
10 1 -7
8 9 -4
9 5 10
3 7 11
3 8 20

输出:
(1, 2, 3)
(1, 10, -7)
(2, 1, 3)
(2, 4, 4)
(3, 4, 5)
(3, 7, 11)
(3, 8, 20)
(4, 2, 4)
(4, 3, 5)
(5, 6, 1)
(5, 9, 10)
(6, 5, 1)
(7, 9, 0)
(7, 3, 11)
(8, 9, -4)
(8, 3, 20)
(9, 7, 0)
(9, 8, -4)
(9, 5, 10)
(10, 1, -7)



*/
struct node_03 {
	int v; //连接的点
	int w; //记录这条边的权值
};
vector <node_03> G_03[11];


//插入有向边

void insert1_03(int u,int v,int w) {
	node_03 temp;
	temp.v = v;
	temp.w = w;
	G_03[u].push_back(temp);
}

void insert2_03(int u,int v,int w) {
	insert1_03(u,v,w);
	insert1_03(v,u,w);
}

void input_03() {
	int m;
	cin >> m;
	for(int i = 0; i < m; i++) {
		int u,v,w;
		cin >> u >> v >> w;
		insert2_03(u,v,w);
	}
}

void output_03() {
	for(int i = 1; i <= 10; i++) {
		for(int j = 0; j < G_03[i].size(); j++) {
			cout << "(" << i << ", " << G_03[i][j].v << ", " << G_03[i][j].w << ")" <<endl;
		}
	}

}
void test_03() {
	input_03();
	output_03();
	return;
}

/*map <string ,int > m;


*/
#include<map>
int ids_04;
int G_04[500][500];
map<string , int> dict_04;
int find_04(string a) {
	if(dict_04.find(a) == dict_04.end()) {
		dict_04[a] = ++ids_04;
	} else {
		return dict_04[a];
	}
}

void test_04() {
	memset(G_04,0,sizeof(G_04));
	int n;
	cin >> n;
	for(int i = 0; i < n; i++) {
		string a,b;
		cin >> a >> b;
		int x = find_04(a);
		int y = find_04(b);
		G_04[x][y] = G_04[y][x] = 1;
	}
	return;
}

/*
录入数据
查询映射表

输入n对朋友关系,相互的a <-> b

是朋友输出yes ,否则no


#include<string>
map<string , int>dict ;
int cnt;
string name;
dict[name] = cnt++;

void test_05(){

	return;
}
*/



最短路径

dijkstra算法

/*单源最短路径 用DFS

BFS只适用边权值为1的图

迪杰斯特拉:不断加 点入集合 ,判断起点到各个点的距离是否更短 ,更新   不能有负权值!!!  (只用于非负权图)
经常用邻接表存储:

输入:
3 3
1 2 5
2 3 5
3 1 2

输出:
2

*/
const int N_06 = 1001;
const int M_06 = 10001;

struct edge_06 {

	edge_06() {
	}

	edge_06(int _v , int _w,int _next) {   //顶点编号  ,边的权值,这条边的下一条边 (边的编号)    (类似链表存邻接表) 
		v = _v;
		w = _w;
		next = _next;
	}
	int v,w,next;

} e_06[2 * M_06]; //建立一个结构体变量 , 变量名为e_06


int head_06[N_06];//存每个点连接的第一条边 
int size_06;

void init_06() {
	memset(head_06,-1,sizeof(head_06));  //刚开始默认没有边连出   -1表示没有边连接   
	size_06 = 0; //图中的边数
}

void insert_06(int u,int v,int w) {
	e_06[size_06] = edge_06(v,w,head_06[u]);
	head_06[u] = size_06++;
}

void insert2_06(int u,int v,int w) {
	insert_06(u,v,w); //互通无向图
	insert_06(v,u,w);
}

int n_06,m_06;
int dis_06[N_06]; //路径距离
bool vis_06[N_06];
void dijkstra_06(int u) {
	memset(vis_06,false,sizeof(vis_06));//比循环快 ,逐字节赋值 8bits  2个16进制位   0x()()
	memset(dis_06,0x3f,sizeof(dis_06)) ; //初始化为一个大数,保证更新  0x3f够大!!!
	dis_06[u] = 0;
	for(int i = 0; i < n_06; i++) {
		int mind = 1000000000,minj = -1;  //最小距离  ,有通路
		for(int j = 1; j <= n_06; j++) {
			if(!vis_06[j] && dis_06[j] < mind) {
				minj = j;
				mind = dis_06[j];
			}
		}
		if(minj == -1) {
			return;
		}
		vis_06[minj] = true;
		for(int j = head_06[minj]; ~j; j = e_06[j].next ) { //遍历minj出发的边    ~j 等价于 j != -1
			int v = e_06[j].v;
			int w = e_06[j].w;
			if( !vis_06[v] && dis_06[v] > dis_06[minj]  + w) { //v这个点没有被标记过 ,到v这个点距离比 dis_06[min]  + w距离还大,更新
				dis_06[v] = dis_06[minj] + w;
			}

		}
	}
}
void test_06() {
	init_06();
	int u,v,w;
	cin >> n_06 >> m_06;
	while(m_06--) {
		cin >> u >> v >> w;
		insert2_06(u,v,w);
	}
	dijkstra_06(1);
	cout << dis_06[n_06] << endl;

	return;
}

SPFA算法


/*
SPFA算法  :一种单源路径算法     用队列  类似BFS    相邻点入队,每个点的相邻点入队 ,点判断完出队 ,更新
(Shortest Path Faster Algorithm 最短路径最快算法)
可以用于有负权值的图

输入:
3 3
1 2 5
2 3 5
3 1 2

输出:
2


*/

#include<queue>
#include<cstring>

const int N = 1e3 + 9;
const int M = 1e4 + 9;
const int inf = 0x3f3f3f3f;

//---------------------------------邻接表的创建 
struct edge {
	int v,w,fail;  // v顶点编号    fail即边的编号    
	edge() {
	}
	edge(int _v , int _w,int _fail) {
		v = _v;
		w = _w;
		fail = _fail;
	}

} e[M << 1];  //M * 2

int head[N];
int len;

void init() {
	memset(head,-1,sizeof(head));
	len = 0;
}

void add(int u,int v,int w) {
	e[len] = edge(v,w,head[u]);  //初始len == 0 统计边数,每加一条边len++ 
	head[u] = len++;//边的编号 
}

void add2(int u,int v,int w) {//无向图 
	add(u,v,w);
	add(v,u,w);
}
//--------------------------------- 
int m,n;
int dis[N];  //存放距离
bool vis[N];  //标记有没有在队列里
void spfa(int u) {//从起点开始 , 一趟遍历完所有点 
	memset(vis,false,sizeof(vis)); //初始化 距离数组 ,标记数组
	vis[u] = true; //表示 放入队列里  
	memset(dis,0x3f,sizeof(dis));   //0x3f3f3f3f 和0x3f一样 ,memset只会截取一个字节 
	dis[u] = 0;  //自己到自己为0
	queue<int> q;
	q.push(u); //判断点入队
	while(!q.empty()) { //所有顶点遍历完,均出队,队空结束
		u = q.front();  //顶点队列顺序 判断
		q.pop(); //出队
		vis[u] = false;  //判断完出队  false表示不在队列里 
		for(int j = head[u]; ~j; j = e[j].fail) { //j遍历u的所有边  , j = e[j].fail即下一条边   ~j 即 j != -1 最后一条边后没有边为-1 
			int v = e[j].v;
			int w = e[j].w;
			if(dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				if(!vis[v]) {
					q.push(v);   // 当前遍历的顶点的相邻点  没有被访问就放入队列
					vis[v] = true; //表示遍历过
				}
			}
		}
	}
}



void test_07() {
	init();
	int u,v,w;
	cin >> n >> m; // 顶点数 、 边数 
	while(m--) {
		cin >> u >> v >> w;  //顶点 、 下一个顶点(边的左右顶点) 、 权值 
		add2(u,v,w); //录入e[]数组 存边,  head[]数组 存 
	}
	spfa(1); //从编号1开始遍历
	cout << dis[n] << endl;
	return;
}


int main() {

	test_07();

	return 0;

}

多源最短路径 (Floyd 算法)


/*
SPFA 判断负环 :如果出队次数大于n,说明有负环 ,无法确定最短路径

*/

/*多源最短路
Floyd 算法

dp[k][i][j];  //从i到j 经过 1 到 k点的最短路径 是多少;

如 i j k 三点 : 
i -- > j
①若不经过k最短  则为 dp[k - 1][i][j]

②若经过k最短  则为 dp[k - 1][i][k] + dp[k - 1][k][j]  


输入:
3 3
1 2 5
2 3 5
3 1 2

输出:
0 5 2
5 0 5
2 5 0

*/

const int N = 101;
int g[N][N]; //最终存放每两点之间最短距离 矩阵  
void floyd(int n) {
	for(int k = 1;k <= n;k++) { //由dp知 k在最外层 
		for(int i = 1;i <= n;i++) {
			for(int j = 1;j <= n;j++) {
				g[i][j] = min(g[i][j] , g[i][k] + g[k][j]); //状态转移方程  降维 
			}//用三个点比较距离来记
		}
	}
}
void test_08() {
	memset(g,0x3f,sizeof(g)); //初始g元素为最大值 ,保证更新 
	for(int i = 0; i < N; i++) {
		g[i][i] = 0; //初始化对角线都为0 
	}
	int n,m; //顶点 、 边 
	int u,v,w;
	cin >> n >> m;
	while(m--) {
		cin >> u >> v >> w;
		g[u][v] = g[v][u] = w; //输入无向图 边权值 
	}
	floyd(n); //调用算法 
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= n;j++){
			cout << g[i][j] << " ";
		}
		cout << endl;
	} 
	return;
}


int main() {

	test_08();

	return 0;

}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值