自己总结的关于图论一堆的模板

不说废话直接上

有那么亿点多

共37488个字符

//dfs 走迷宫对应的伪代码框架如下:
// 对坐标为 (x, y) 的点进行搜索
bool dfs(int x, int y) {
	if (x, y) 是终点 {
		// 找到了路径
		return true;
	}
	标记 (x, y) 已经访问
	向上走到位置 (tx, ty)
	if (tx, ty) 合法 {
		if (dfs(tx, ty) == true) {
			// 递归调用dfs函数,将(tx, ty)作为当前状态进行搜索,下同。
			return true;
		}
	}
	向左走到位置 (tx, ty)
	if (tx, ty) 合法 {
		if (dfs(tx, ty) == true) {
			return true;
		}
	}
	向下走到位置 (tx, ty)
	if (tx, ty) 合法 {
		if (dfs(tx, ty) == true) {
			return true;
		}
	}
	向右走到位置 (tx, ty)
	if (tx, ty) 合法 {
		if (dfs(tx, ty) == true) {
			return true;
		}
	}
	return false;
}
//----------------------------------------------------------------------------
//连通块问题 DFS
void dfs(int x, int y) {
	cnt++; // 这个连通块的大小
	vis[x][y] = true;
	for (int i = 0; i < 4; i++) {
		int tx = x + dir[i][0];
		int ty = y + dir[i][1];
		if (in(tx, ty) && !vis[tx][ty] && maze[tx][ty] == '*') {
			dfs(tx, ty);
		}
	}
}
//然后调用的时候是这样
for (int i = 0; i < n; i++) {
	for (int j = 0; j < m; j++) {
		if (maze[i][j] == '*' && !vis[i][j]) {
			ans++; // 连通块数量
			cnt = 0; // 这个连通块的大小
			dfs(i, j);
			mx = max(mx, cnt); // 求最大连通块
		}
	}
}
//----------------------------------------------------------------------------
//BFS
void bfs(起始点) {
	将起始点放入队列中;
	标记起点访问;
	while (如果队列不为空) {
		访问队列中队首元素x;
		删除队首元素;
		for (x 所有相邻点) {
			if (该点未被访问过且合法) {
				将该点加入队列末尾;
			}
		}
	}
	队列为空,广搜结束;
}
---------------------------------
int bfs(int sx,int sy)
{
	q.push((Pos){sx,sy}); //起点加入队列
	vis[sx][sy]=true; //标记
	while(!q.empty()) 
	{
		x=q.front().x;
		y=q.front().y; //获取起始坐标
		q.pop(); //弹出队列
		if(符合条件) return ans(答案); 
		for(int i=0;i<走法;i++)
		{
			tx=x+dx[i];
			ty=y+dy[i];
			if(符合条件) continue;
			if(符合条件) continue; //符合条件跳过循环
			/*可行,执行该部分语句*/
			q.push((Pos){tx,ty}); //加入队列
		}
	}
}
//----------------------------------------------------------------------------
//
//邻接表存图
struct node {
	int v;  // 用来记录连接的点
	int len;  // 用来记录这条边的边权
};
vector<node> G[110];
// 插入有向边
void insert1(int u, int v, int w) {
	node temp;
	temp.v = v;
	temp.len = w;
	G[u].push_back(temp);
}
// 插入无向边
void insert2(int u, int v, int w) {
	insert1(u, v, w);
	insert1(v, u, w);
}
//----------------------------------------------------------------------------
//图上DFS
void dfs(int u) {
	cout << u << endl;
	vis[u] = true;
	for (int i = 0; i < G[u].size(); i++) {
		if (!vis[G[u][i]]) {
			dfs(G[u][i]);
		}
	}
}
//----------------------------------------------------------------------------
//图上BFS
void bfs(int u) {
	q.push(u);
	vis[u] = true;
	while (!q.empty()) {
		int now = q.front();
		q.pop();
		cout << now << endl;
		for (int i = 0; i < G[now].size(); i++) {
			if (!vis[G[now][i]]) {
				q.push(G[now][i]);
				vis[G[now][i]] = true;
			}
		}
	}
}
//----------------------------------------------------------------------------
//树上DFS
void dfs(int u, int fa) { // fa 就是父节点
	cout << u << endl;
	for (int i = 0; i < G[u].size(); i++) {
		int v = G[u][i];
		if (v != fa) {
			dfs(v, u); // 那往下搜索,到 v 的时候父节点就是我们目前在的节点 u
		}
	}
}
//----------------------------------------------------------------------------
//基于链表的邻接表的实现(链式前向星)
const int M = 1000000;
const int N = 10000;
struct edge {
	int v, d, next;
} e[M];
int p[N], eid;
void init() {  // 初始化,在建图之前必须进行
	memset(p, -1, sizeof(p));
	eid = 0;
}
void insert(int u, int v, int d) {  // 插入单向边
	e[eid].v = v;
	e[eid].d = d;
	e[eid].next = p[u];
	p[u] = eid++;
}
void insert2(int u, int v, int d) {  // 插入双向边
	insert(u, v, d);
	insert(v, u, d);
}
void output(int n) {  // 输出整张图中的所有边
	for (int i = 0; i < n; i++) {
		for (int j = p[i]; j != -1; j = e[j].next) {  // 遍历从 i 连出的所有边
			cout << i << "->" << e[j].v << ", " << e[j].d << endl;
		}
	}
}
//----------------------------------------------------------------------------
//记忆化搜索模板 
/*
f(problem p){
	if(p has been solved){
		 return the result
	}else{
		 divide the p into some sub-problems (p1, p2, p3...)
		 f(p1);
		 f(p2);
		 f(p3);
		 ...
	}
*/
//----------------------------------------------------------------------------
//堆支持以下的基本操作:
//a) 插入:向堆中插入一个新元素;在数组的最末尾插入新结点。然后自下而上调整子结点与父结点:比较当前结点与父结点,不满足堆性质则交换,使得当前子树满足二叉堆的性质。时间复杂度为O(logn)。
void push(int A[], int i, int& n) {  // i 为插入的值, n 为插入之前堆的大小
	n++; // 调整大小
	A[n] = i; // 放进堆的最后
	int p = n;
	while (p > 1 && A[p / 2] > A[p]) { // 调整,如果不满足堆的性质,交换父节点和当前节点
		swap(A[p / 2], A[p]);
		p /= 2;
	}
}
//b) 弹出:删除堆顶元素,再把堆存储的最后那个结点填在根结点处。再从上而下调整父结点与它的子结点。时间复杂度为O(logn)。
int pop(int A[], int& n) {
	int res = A[1]; // 记录堆顶元素
	A[1] = A[n]; // 把堆顶元素换到最后
	n--; // 调整大小,此时原来的最后一位虽然有值但不会再用了
	int p = 1, t;
	while (p * 2 <= n) { // 调整
		if (p * 2 + 1 > n || A[p * 2] <= A[p * 2 + 1]) { // 找到左右两个孩子中的较小者
			t = p * 2;
		} else {
			t = p * 2 + 1;
		}
		if (A[p] > A[t]) { // 如果不满足堆的性质就交换
			swap(A[p], A[t]);
			p = t;
		} else { // 否则就调整完成了
			break;
		}
	}
	return res;
}
//c) 删除:使该元素与堆尾元素交换,调整堆容量,再由原堆尾元素的当前位置自顶向下调整。时间复杂度为 O(logn)。因此,堆支持查询最值、插入、删除操作。
//----------------------------------------------------------------------------
//优先队列的使用
#include <queue>
#include <iostream>
using namespace std;
int main() {
	priority_queue<int> q; // 声明一个装 int 类型数据的优先队列
	q.push(1); // 入队
	q.push(2);
	q.push(3);
	while (!q.empty()) { // 判断队列是否为空
		cout << q.top() << endl; // 访问队列首元素
		q.pop(); // 出队
	}
	return 0;
}
/*
16
输出为
17
3
18
2
19
1
20
*/
//优先队列的优先级重载
struct node {
	int dist, loc;
	node() { }
	bool operator < (const node & a) const {
		return dist > a.dist;
	}
};
priority_queue <node> Q;
//优先队列默认会把较大的数据放在队首,也就是它是大根堆,想要把较小的数据放在前边需要在声明的时候写成这样:
priority_queue <int, vector<int>, greater<int> > pq;
//----------------------------------------------------------------------------
//存储int类型的堆:
#define PII pair<int, int>
set<PII, greater<PII>> gheap;  // 定义了一个大根堆
set<PII, less<PII>> lheap;  // 定义了一个小根堆
int keys[MAX_INDEX];  // 存储每个索引对应的关键字,如果索引的范围很大,可以用 map<int, int> 来储存
//set默认顺序为从小到大,所以声明小根堆时,第二个参数less<PII>可省。
//其中pair<int, int>的first储存关键字,second储存原始的索引(或下标)。
//堆的插入
//使用如下的方法将关键字为value、索引为id的元素插入堆中。
gheap.insert(make_pair(value, id));
keys[id] = value;
//获取及删除堆顶元素
//我们可以在O(logn) 的时间复杂度内获取对应元素的关键字和索引。
set<PII, greater<PII>>::iterator iter = gheap.begin();
cout << iter->first << " " << iter->second << endl;  // 第一个数是堆顶元素的关键字,第二个数是堆顶元素的索引
//并在O(logn) 的时间复杂度内删除堆顶元素。
gheap.erase(gheap.begin());
//删除指定索引
//我们可以在O(logn) 的时间复杂度内将堆中指定索引idx的元素删除。
gheap.erase(make_pair(keys[idx], idx));
//修改指定索引的值可以先删除该索引,再插入索引为该索引,值为新的值的元素。
//----------------------------------------------------------------------------
//二叉树的遍历
int son[N][2], root;
vector<int> v1, v2, v3, v4;
//先序遍历
void preorder(int u) {
	if (u == 0) return;
	v1.push_back(u);
	preorder(son[u][0]);
	preorder(son[u][1]);
}
//中序遍历
void inorder(int u) {
	if (u == 0) return;
	inorder(son[u][0]);
	v2.push_back(u);
	inorder(son[u][1]);
}
//后序遍历
void postorder(int u) {
	if (u == 0) return;
	postorder(son[u][0]);
	postorder(son[u][1]);
	v3.push_back(u);
}
//层次遍历
void levelorder() {
	queue<int> q;
	if (root != 0) q.push(root);
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		v4.push_back(u);
		if (son[u][0] != 0) q.push(son[u][0]);
		if (son[u][1] != 0) q.push(son[u][1]);
	}
}
//----------------------------------------------------------------------------
//已知中序遍历和先序遍历构建二叉树的参考代码:
// a数组为中序遍历,b数组为先序遍历
// x1、y1为当前子树在a数组中下标的范围,x2为前子树在b数组中下标的起点
int a[N], b[N], son[N][2];
void dfs(int x1, int y1, int x2) {
	int pos = x1;
	while (a[pos] != b[x2]) { // 在中序遍历里找到当前子树的根
		pos++;
	}
	int len1 = pos - x1, len2 = y1 - pos; // 在中序遍历里确定当前子树的左子树和右子树大小
	if (len1 > 0) { // 如果有左子树,那么根据先序遍历确定这个节点的左孩子
		son[b[x2]][0] = b[x2 + 1];
		dfs(x1, pos - 1, x2 + 1); // 递归进入左子树
	}
	if (len2 > 0) { // 如果有右子树,那么根据先序遍历确定这个节点的右孩子
		son[b[x2]][1] = b[x2 + 1 + len1];
		dfs(pos + 1, y1, x2 + 1 + len1); // 递归进入右子树
	}
}
//----------------------------------------------------------------------------
//Floyd
int g[N][N];
void floyd(int n) {
	for (int k = 1; k <= n; 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]);
			}
		}
	}
}
//----------------------------------------------------------------------------
//Dijkstra
for (int i = 1; i <= n; i++) {
	int mind = inf; // 这一次的最小距离
	int v = 0; // 这一次最近的点
	for (int j = 1; j <= n; j++) {
		if (!vis[j] && d[j] < mind) { // 找到没有用过的最近的点
			mind = d[j];
			v = j;
		}
	}
	if (mind == inf) { // 如果最小距离是 inf 就说明剩下的点都不跟起点连通
		break;
	}
	vis[v] = true; // 把这一次的点标记
	for (int j = 0; j < g[v].size(); j++) { // 更新最短距离
		d[g[v][j].v] = min(d[g[v][j].v], d[v] + g[v][j].w);
	}
}
//----------------------------------------------------------------------------
//实现Dijkstra
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1001;
const int inf = 0x3f3f3f3f;
struct node {
	int v, w;
	node() {
	  
	}
	node(int vv, int ww) {
		v = vv;
		w = ww;
	}
};
vector<node> g[N];
int n, m, s;
int d[N];
bool vis[N];
int main() {
	cin >> n >> m >> s;
	for (int i = 0; i < m; i++) {
		int u, v, w;
		cin >> u >> v >> w;
		g[u].push_back(node(v, w));
		g[v].push_back(node(u, w));
	}
	memset(d, 0x3f, sizeof(d));
	d[s] = 0;
	for (int i = 1; i <= n; i++) {
		int mind = inf; // 这一次的最小距离
		int v = 0; // 这一次最近的点
		for (int j = 1; j <= n; j++) {
			if (!vis[j] && d[j] < mind) { // 找到没有用过的最近的点
				mind = d[j];
				v = j;
			}
		}
		if (mind == inf) { // 如果最小距离是 inf 就说明剩下的点都不跟起点连通
			break;
		}
		vis[v] = true; // 把这一次的点标记
   		for (int j = 0; j < g[v].size(); j++) { // 更新最短距离
			d[g[v][j].v] = min(d[g[v][j].v], d[v] + g[v][j].w);
		}
	}
	for (int i = 1; i <= n; i++) {
		cout << d[i] << " ";
	}
	return 0;
}
//----------------------------------------------------------------------------
//小根堆优化的 Dijkstra代码:
set<pair<int, int> > min_heap;
min_heap.insert(make_pair(0, s));
while (min_heap.size()) {
	int mind = min_heap.begin() -> first; // 取出最小距离
	int v = min_heap.begin() -> second; // 取出最近的点
	min_heap.erase(min_heap.begin()); // 删除首元素
	for (int i = 0; i < g[v].size(); i++) {
		if (d[g[v][i].v] > d[v] + g[v][i].w) { // 更新
			min_heap.erase(make_pair(d[g[v][i].v], g[v][i].v)); // 先删除原来的元素
			d[g[v][i].v] = d[v] + g[v][i].w; // 更新距离
			min_heap.insert(make_pair(d[g[v][i].v], g[v][i].v)); // 加入新的元素
		}
	}
}
//----------------------------------------------------------------------------
//堆优化的 Dijkstra 代码
priority_queue <pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > min_heap;
min_heap.push(make_pair(0, s));
while (min_heap.size()) {
	int mind = min_heap.top().first;
	int v = min_heap.top().second;
	min_heap.pop();
	if (mind != d[v]) { // 如果距离被更新了不再是这个元素的距离了,那么就不用考虑这个元素了
		continue;
	}
	for (int i = 0; i < g[v].size(); i++) {
		if (d[g[v][i].v] > d[v] + g[v][i].w) { // 更新
			d[g[v][i].v] = d[v] + g[v][i].w;
			min_heap.push(make_pair(d[g[v][i].v], g[v][i].v)); // 把新元素放入优先队列
		}
	}
}
//----------------------------------------------------------------------------
//Dijkstra 代码(到每一个点 
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
#include <set>
/*
priority_queue <pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > min_heap;
min_heap.push(make_pair(0, s));
while (min_heap.size()) {
	int mind = min_heap.top().first;
	int v = min_heap.top().second;
	min_heap.pop();
	if (mind != d[v]) { // 如果距离被更新了不再是这个元素的距离了,那么就不用考虑这个元素了
		continue;
	}
	for (int i = 0; i < g[v].size(); i++) {
		if (d[g[v][i].v] > d[v] + g[v][i].w) { // 更新
			d[g[v][i].v] = d[v] + g[v][i].w;
			min_heap.push(make_pair(d[g[v][i].v], g[v][i].v)); // 把新元素放入优先队列
		}
	}
}
*/
using namespace std;
const int N = 100001;
const int inf = 0x3f3f3f3f;
struct node {
	int v, w;
	node() {
	  
	}
	node(int vv, int ww) {
		v = vv;
		w = ww;
	}
};
vector<node> g[N];
int n, m, s;
int d[N];
set<pair<int, int> > min_heap;
int main() {
	cin >> n >> m >> s;
	for (int i = 0; i < m; i++) {
		int u, v, w;
		cin >> u >> v >> w;
		g[u].push_back(node(v, w));
		g[v].push_back(node(u, w));
	}
	memset(d, 0x3f, sizeof(d));
	d[s] = 0;
	min_heap.insert(make_pair(0,s));
	while (min_heap.size()) {
		int mind = min_heap.begin()->first;
		int v = min_heap.begin()->second;
		min_heap.erase(min_heap.begin());
		for (int i = 0; i < g[v].size(); i++) {
			if (d[g[v][i].v] > d[v] + g[v][i].w) {
				min_heap.erase(make_pair(d[g[v][i].v], g[v][i].v));
				d[g[v][i].v] = d[v] + g[v][i].w;
				min_heap.insert(make_pair(d[g[v][i].v],g[v][i].v));
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		cout << d[i] << " ";
	}
	return 0;
}
//----------------------------------------------------------------------------
//SPFA示例代码
bool in_queue[MAX_N];
int d[MAX_N];  // 如果到顶点 i 的距离是 0x3f3f3f3f,则说明不存在源点到 i 的最短路
queue<int> q;
void spfa(int s) {
	memset(in_queue, 0, sizeof(in_queue));
	memset(d, 0x3f, sizeof(d));
	d[s] = 0;
	in_queue[s] = true; // 标记 s 入队
	q.push(s);
	while (!q.empty()) {
		int v = q.front();
		q.pop();
		in_queue[v] = false;
		for (int i = 0; i < g[v].size(); i++) {
			int x = g[v][i].v;
			if (d[x] > d[v] + g[v][i].w) { // 更新
				d[x] = d[v] + g[v][i].w;
				if (!in_queue[x]) { // 如果之前没入队
					q.push(x); // 入队
					in_queue[x] = true; // 标记 x 入队
				}
			}
		}
	}
}
//----------------------------------------------------------------------------
//SPFA判断负环
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100001;
const int inf = 0x3f3f3f3f;
struct node {
	int v, w;
	node() {
	  
	}
	node(int vv, int ww) {
		v = vv;
		w = ww;
	}
};
vector<node> g[N];
int n, m, s;
int d[N], cnt[N];
bool in_queue[N];
queue<int> q;
bool spfa(int s) {
	memset(d, 0x3f, sizeof(d));
	d[s] = 0;
	in_queue[s] = true;
	q.push(s);
	cnt[s]++;
	while (!q.empty()) {
		int v = q.front();
		q.pop();
		in_queue[v] = false;
		for (int i = 0; i < g[v].size(); i++) {
			int x = g[v][i].v;
			if (d[x] > d[v] + g[v][i].w) {
				d[x] = d[v] + g[v][i].w;
				if (!in_queue[x]) {
					q.push(x);
					in_queue[x] = true;
					cnt[x]++;
					if(cnt[x]>n)
					{
						return true;
					}
				}
			}
		}
	}
	return false;
}
int main() {
	cin >> n >> m >> s;
	for (int i = 0; i < m; i++) {
		int u, v, w;
		cin >> u >> v >> w;
		g[u].push_back(node(v, w));
	}
	if (spfa(s)) {
		cout << "YES" << endl;
	} else {
		cout << "NO" << endl;
	}
	return 0;
}
//----------------------------------------------------------------------------
//并查集
//1) 初始化:初始的时候每个节点各自为一个集合fa[i]表示节点i的父亲节点,如果fa[i]=i,我们认为这个节点是当前集合根节点。
void init() {
	for (int i = 1; i <= n; i++) {
		fa[i] = i;
	}
}
//2) 查找:查找节点所在集合的根节点,节点x的根节点必然也是其父亲节点的根节点。
int get(int x) {
	if (fa[x] == x) { // x 节点就是根节点
		return x;
	}
	return get(fa[x]); // 返回父节点的根节点
}
//3) 合并:将两个元素所在的集合合并在一起,实际上就是把两个元素的根节点进行合并。通常来说,合并之前先判断两个元素是否属于同一集合。
void merge(int x, int y) {
	x = get(x);
	y = get(y);
	if (x != y) { // 不在同一个集合
		fa[y] = x;
	}
}
//----------------------------------------------------------------------------
//并查集路径压缩
int get(int x) {
	if (fa[x] == x) { // x 结点就是根结点
		return x;
	}
	return fa[x] = get(fa[x]); // 返回父结点的根结点,并令当前结点父结点直接为根结点
}
//----------------------------------------------------------------------------
//基于邻接表的拓扑排序代码:
void topo() {
	queue<int> q;
	for (int i = 1; i <= n; i++) {
		  if (indegree[i] == 0) {  // 将所有入度为零的顶点入队
			q.push(i);
		}
	}
	while (!q.empty()) {
		int now = q.front();  // 每次取出队首元素
		cout << "visiting " << now << endl;
		q.pop();
		for (int i = 0; i < g[now].size(); i++) {
			int v = g[now][i];
			indegree[v]--;
			if (indegree[v] == 0) {  // 将入度新变成零的顶点入队
				q.push(v);
			}
		}
	}
}
//----------------------------------------------------------------------------
//无向图欧拉路径
//计算无向图的欧拉路经可以用“套圈法”、基于深度优先搜索来实现。从一个合适的顶点出发进行深度优先搜索。当搜到一个顶点 uu 时:如果此时没有顶点与该顶点相连,那么就将 uu 加入路径中并回溯;如果有顶点 vv 与顶点 uu 相连,那么就删除无向边 <u, v><u,v>,并继续搜索顶点 vv。将所有和 uu 相邻的顶点遍历完之后,将 uu 加入路径中。
const int N = 105;
const int M = 10005;
int degree[N];  // 表示顶点剩余的度
int n;  // 顶点个数
struct node {
	int v; // 连接的顶点
	int id; // 边的编号
};
bool vis[M]; // 边是不是被访问过
vector<node> G[N];
void dfs(int u) {
	if (degree[u]) {
		for (int i = 0; i < G[u].size(); i++) {
			if (!vis[G[u][i].id]) {
				vis[G[u][i].id] = true;
				degree[u]--;
				degree[G[u][i].v]--;
				dfs(G[u][i].v);
			}
		}
	}
	cout << u << endl;
}
//----------------------------------------------------------------------------
//有向图欧拉路径
//计算有向图的欧拉路经同样可以用“套圈法”。和无向图唯一不同的是,在记录方案的时候将顶点编号插入栈中,并在最后将栈中的元素依次输出。也就是说,将之前输出的顺序逆置。
const int N = 105;
const int M = 10005;
int outdegree[N];  // 表示顶点剩余的度
int n;  // 顶点个数
struct node {
	int v; // 连接的顶点
	int id; // 边的编号
};
bool vis[M]; // 边是不是被访问过
vector<node> G[N];
stack<int> s;
void dfs(int u) {
	if (outdegree[u]) {
		for (int i = 0; i < G[u].size(); i++) {
			if (!vis[G[u][i].id]) {
				vis[G[u][i].id] = true;
				outdegree[u]--;
				dfs(G[u][i].v);
			}
		}
	}
	s.push(u);
}
//----------------------------------------------------------------------------
//Kruskal 最小生成树算法代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N = 100000;  // 最大顶点数
const int MAX_M = 100000;  // 最大边数
struct Edge {
	int u, v, len;
}e[MAX_M];
int fa[MAX_N], n, m;  // fa 数组记录了并查集中结点的父亲
bool cmp(Edge a,Edge b) {
	return a.len < b.len;
}
// 并查集相关代码
void init() {
	for (int i = 1; i <= n; i++) {
		fa[i] = i;
	}
}
int get(int x) {
	if (fa[x] == x) {
		return x;
	}
	return fa[x] = get(fa[x]);
}
int kruskal(int n, int m) {
	sort(e + 1, e + m + 1, cmp);  // 对边按边权进行升序排序
	init(); // 初始化并查集
	int ans = 0;  // 保存最小生成树上的总边权和,也就是答案
	for (int i = 1; i <= m; i++) {
		int fu = get(E[i].u);
		int fv = get(E[i].v);
		if (fu != fv) { // 合并
			fa[fv] = fu;
			ans += e[i].len;
		}
	}
	return ans;
}
int main() {
	cin >> n >> m;  // n 为点数,m 为边数
	for (int i = 1; i <= m; i++) {
		cin >> e[i].u >> e[i].v >> e[i].len;  // 用边集数组存放边,方便排序和调用
	}
	cout << kruskal(n, m) << endl;
	return 0;
}
//----------------------------------------------------------------------------
//Prim最小生成树 
#include<bits/stdc++.h>
using namespace std;
int n,m,g[550][550],dist[550],a,b,c;
bool st[550];
int prim(){
	memset(dist,0x3f,sizeof dist);
	int res=0;
	for(int i=0;i<n;i++){
		int t=-1;
		for(int j=1;j<=n;j++)if(!st[j]&&(t==-1||dist[t]>dist[j]))t=j;
		if(i&&dist[t]==0x3f3f3f3f)return 0x3f3f3f3f;
		if(i)res+=dist[t];
		st[t]=true;
		for(int j=1;j<=n;j++)dist[j]=min(dist[j],g[t][j]);
	}
	return res;
}
int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m;
	memset(g,0x3f,sizeof g);
	while(m--){
		cin>>a>>b>>c;
		g[a][b]=g[b][a]=min(g[a][b],c);
	}
	int t=prim();
	if(t==0x3f3f3f3f)cout<<"impossible"<<endl;
	else cout<<t<<endl;
	return 0;
}
//----------------------------------------------------------------------------
//树的直径
int maxlen, point; // 当前最远距离和得到最远距离的点
void dfs(int u, int pre, int step) {
	if (step > maxlen) {
		maxlen = step;
		point = u;
	}
	for (int i = p[u]; i != -1; i = E[i].next) {
		if (E[i].v != pre) {
			dfs(E[i].v, u, step + 1);
		}
	}
}
int diameter() {
	maxlen = -1;
	dfs(1, 0, 0);
	maxlen = -1;
	dfs(point, 0, 0);
	return maxlen;
}
//----------------------------------------------------------------------------
//树的重心
int minNode = -1, minBalance = MAX_N;
int dfs(int u, int pre) {
	int sz = 0, maxSubtree = 0;
	for (int i = p[u]; i != -1; i = E[i].next) {
		if (E[i].v != pre) {
			int son = dfs(E[i].v, u);
			sz += son;
			maxSubtree = max(maxSubtree, son);
		}
	}
	sz++;
	maxSubtree = max(maxSubtree, n - sz);
	if(maxSubtree < minBalance) {
		minBalance = maxSubtree;
		minNode = u;
	}
	return sz;
}
//----------------------------------------------------------------------------
//RMQ问题
//我们先来看一个经典问题:RMQ问题(Range Minimum/Maximum Query),即给定包含n个数的数组和m个询问,每个询问查询区间[l,r]中最大或最小的数。
#include <cmath>
#include <iostream>
using namespace std;
const int maxn = 1000005;
int n, m, a[maxn], f[maxn][25], l, r;
void prepare() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		f[i][0] = a[i];
	}
	for (int j = 1; (1 << j) <= n; j++) {
		for (int i = 1; i + (1 << j) - 1 <= n; i++) {
			f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
		}
	}
}
int query(int l, int r) {
	int i = (int)(log2(r - l + 1));
	return max(f[l][i], f[r - (1 << i) + 1][i]);
}
int main() {
	prepare();
	for (int i = 1; i <= m; i++) {
		cin >> l >> r;
		cout << query(l, r) << endl;
	}
	return 0;
}
//----------------------------------------------------------------------------
//LCA问题及其倍增解法 
#include <cstring>
#include <iostream>
using namespace std;
const int MAX_N = 100005;
const int MAX_M = 200005;
struct Edge {
	int v, next;
} e[MAX_M];
int p[MAX_N], eid;
void init() {
	memset(p, -1, sizeof(p));
	eid = 0;
}
void insert(int u, int v) {
	e[eid].v = v;
	e[eid].next = p[u];
	p[u] = eid++;
}
int f[MAX_N][20], d[MAX_N];
void dfs(int u) { // 深搜求深度
	d[u] = d[f[u][0]] + 1;
	for (int i = p[u]; i != -1; i = e[i].next) {
		int v = e[i].v;
		if (v == f[u][0]) {
			continue;
		}
		f[v][0] = u;
		dfs(v);
	}
}
int lca(int x, int y) {
	if (d[x] < d[y]) {
		swap(x, y);
	}
	int K = 0;
	while ((1 << (K + 1)) <= d[x]) {
		K++;
	}
	for (int j = K; j >= 0; j--) {
		if (d[f[x][j]] >= d[y]) {
			x = f[x][j];
		}
	}
	if (x == y) {
		return x;
	}
	for (int j = K; j >= 0; j--) {
		if (f[x][j] != f[y][j]) {
			x = f[x][j];
			y = f[y][j];
		}
	}
	return f[x][0];
}
int main() {
	int n, q;
	cin >> n >> q;
	init();
	for (int i = 1; i < n; i++) {
		int u, v;
		cin >> u >> v;
		insert(u, v);
		insert(v, u);
	}
	dfs(1);
	for (int j = 1; (1 << j) <= n; j++) { // 预处理每个点的 2 次幂祖先
		for (int i = 1; i <= n; i++) {
			f[i][j] = f[f[i][j - 1]][j - 1];
		}
	}
	while (q--) {
		int x, y;
		cin >> x >> y;
		cout << lca(x, y) << endl;
	}
	return 0;
}
//----------------------------------------------------------------------------
//时间戳 
int times = 0;
int dfn[maxn];
void dfs(int u) {
	dfn[u] = ++times;
	for (int i = p[u]; i != -1; i = E[i].next) {
		int v = E[i].v;
		if (dfn[v] == 0) {
			dfs(v);
		}
	}
}
//----------------------------------------------------------------------------
//求割点
int times = 0;
int dfn[maxn], low[maxn];
bool iscut[maxn];  // 标记是否是割点
void dfs (int u, int fa) {
	dfn[u] = low[u] = ++times;
	int child = 0;  // 用来处理根结点子结点数
	for (int i = p[u]; i != -1; i = E[i].next) {
		int v = E[i].en;
		if (dfn[v] == 0) {  // v 没有被访问过,u, v 是树边
			++child;
			dfs(v, u);
			low[u] = min(low[u], low[v]);
			if (low[v] >= dfn[u]) {
				iscut[u] = true;
			}
		} else if (dfn[v] < dfn[u] && v != fa) {  // 反向边,注意 v == fa 的时候,是访问重复的边
			low[u] = min(low[u], dfn[v]);
		}
	}
	if (fa < 0 && child == 1) {
		// fa < 0 表示根结点,之前根结点一定会被标记为割点, 取消之
		iscut[u] = false;
	}
}
//注意,调用上面的函数,初始fa参数必须传入一个负数,我们一般传入-1,比如dfs(1, -1)。
//----------------------------------------------------------------------------
//求点双连通分量
int times = 0;
int dfn[maxn], low[maxn];
int bcc_cnt = 0;  // 点双连通分量数量
bool iscut[maxn];  // 标记是否是割点
set<int> bcc[maxn];  // 记录每个点双连通分量里面的点
stack<edge> S;
void dfs (int u, int fa) {
	dfn[u] = low[u] = ++times;
	int child = 0;  // 用来处理根结点子结点数
	for (int i = p[u]; i != -1; i = E[i].next) {
		int v = E[i].v;
		if (dfn[v] == 0) {  // v 没有被访问过,u, v 是树边
			S.push(E[i]);
			++child;
			dfs(v, u);
			low[u] = min(low[u], low[v]);
			if (low[v] >= dfn[u]) {
				iscut[u] = true;
				++bcc_cnt;  // 增加一个点双连通分量
				while (true) {
					edge x = S.top();
					S.pop();
					bcc[bcc_cnt].insert(x.u);
					bcc[bcc_cnt].insert(x.v);
					if (x.u == u && x.v == v) {
						break;
					}
				}
			}
		} else if (dfn[v] < dfn[u] && v != fa) {  // 反向边,注意 v == fa 的时候,是访问重复的边
			S.push(E[i]);
			low[u] = min(low[u], dfn[v]);
		}
	}
	if (fa < 0 && child == 1) {
		// fa < 0 表示根结点,之前根结点一定被标记为割点, 取消之
		iscut[u] = false;
	}
}
//----------------------------------------------------------------------------
//求割点和点双连通分量
#include <iostream>
#include <stack>
#include <set>
#include <cstring>
using namespace std;
const int maxm = 1010;  // 最大边数
const int maxn = 110;  // 最大点数
struct edge {
	int u, v;
	int next;
} E[maxm];
int p[maxn], eid = 0;
void init() {
	memset(p, -1, sizeof(p));
	eid = 0;
}
void insert(int u, int v) {
	E[eid].u = u;
	E[eid].v = v;
	E[eid].next = p[u];
	p[u] = eid++;
}
int times = 0;
int dfn[maxn], low[maxn];
int bcc_cnt = 0;
bool iscut[maxn];
set<int> bcc[maxn];
stack<edge> S;
void dfs(int u, int fa) {
	dfn[u] = low[u] = ++times;
	int child = 0;
	for (int i = p[u]; i != -1; i = E[i].next) {
		int v = E[i].v;
		if (dfn[v] == 0) {
			S.push(E[i]);
			++child;
			dfs(v, u);
			low[u] = min(low[u], low[v]);
			if (low[v] >= dfn[u]) {
				iscut[u] = true;
				++bcc_cnt;
				while (true) {
					edge x = S.top();
					S.pop();
					bcc[bcc_cnt].insert(x.u);
					bcc[bcc_cnt].insert(x.v);
					if (x.u == u && x.v == v) {
						break;
					}
				}
			}
		}
		else if (dfn[v] < dfn[u] && v != fa) {
			S.push(E[i]);
			low[u] = min(low[u], dfn[v]);
		}
	}
	if (fa < 0 && child == 1) {
		iscut[u] = false;
	}
}
int main() {
	init();
	int n, m;
	cin >> n >> m;
	for (int i = 0; i < m; ++i) {
		int u, v;
		cin >> u >> v;
		insert(u, v);
		insert(v, u);
	}
	memset(dfn, 0, sizeof(dfn));
	times = bcc_cnt = 0;
	dfs(1, -1);
	cout << bcc_cnt << endl;
	for (int i = 1; i <= bcc_cnt; ++i) {
		for (set<int>::iterator it = bcc[i].begin(); it != bcc[i].end(); ++it) {
			cout << (*it) << " ";
		}
		cout << endl;
	}
	return 0;
}
//----------------------------------------------------------------------------
//求桥和边双连通分量
#include <cstring>
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
const int maxm = 1010;  // 最大边数
const int maxn = 110;   // 最大点数
struct edge {
	int u, v;
	int next;
} E[maxm];
int p[maxn], eid = 0;
void init() {
	memset(p, -1, sizeof(p));
	eid = 0;
}
void insert(int u, int v) {
	E[eid].u = u;
	E[eid].v = v;
	E[eid].next = p[u];
	p[u] = eid++;
}
int times = 0;
int dfn[maxn], low[maxn];
int bcc_cnt = 0;
vector<int> bcc[maxn];
stack<int> S;
void dfs(int u, int fa) {
	dfn[u] = low[u] = ++times;
	S.push(u);
	for (int i = p[u]; i != -1; i = E[i].next) {
		int v = E[i].v;
		if (dfn[v] == 0) {
			dfs(v, u);
			low[u] = min(low[u], low[v]);
		} else if (dfn[v] < dfn[u] && v != fa) {
			low[u] = min(low[u], dfn[v]);
		}
	}
	if (low[u] == dfn[u]) {
		++bcc_cnt;
		while (true) {
			int x = S.top();
			S.pop();
			bcc[bcc_cnt].push_back(x);
			if (x == u) break;
		}
	}
}
int main() {
	init();
	int n, m;
	cin >> n >> m;
	for (int i = 0; i < m; ++i) {
		int u, v;
		cin >> u >> v;
		insert(u, v);
		insert(v, u);
	}
	memset(dfn, 0, sizeof(dfn));
	times = bcc_cnt = 0;
	dfs(1, -1);
	cout << bcc_cnt << endl;
	for (int i = 1; i <= bcc_cnt; ++i) {
		for (int j = 0; j < bcc[i].size(); j++) {
			cout << bcc[i][j] << " ";
		}
		cout << endl;
	}
	return 0;
}
//----------------------------------------------------------------------------
//强连通分量
#include <iostream>
#include <stack>
#include <set>
#include <cstring>
using namespace std;
const int maxm = 1010;
const int maxn = 110;
struct edge {
	int v;
	int next;
} E[maxm];
int p[maxn], eid = 0;
void init() {
	memset(p, -1, sizeof(p));
	eid = 0;
}
void insert(int u, int v) {
	E[eid].v = v;
	E[eid].next = p[u];
	p[u] = eid++;
}
int times = 0;
int dfn[maxn], low[maxn];
int scc_cnt = 0;  // 强连通分量数量
int sccno[maxn];  // 记录每个点属于的强连通分量的编号
set<int> scc[maxn];
stack<int> S;
void dfs (int u) {
	dfn[u] = low[u] = ++times;
	S.push(u);
	for (int i = p[u]; i != -1; i = E[i].next) {
		int v = E[i].v;
		if (dfn[v] == 0) {  // v 没有被访问过,u, v 是树边
			dfs(v);
			low[u] = min(low[u], low[v]);
		} else if (!sccno[v]) {  // 对于已经求出 scc 的点,直接删除
			low[u] = min(low[u], dfn[v]);
		}
	}
	if (low[u] == dfn[u]) {  // u 是第一个被探测到的点
		++scc_cnt;
		while (true) {
			int x = S.top();
			S.pop();
			sccno[x] = scc_cnt;
			scc[scc_cnt].insert(x);
			if (x == u) {
				break;
			}
		}
	}
}
int main() {
	init();
	int n, m;
	cin >> n >> m;
	for (int i = 0; i < m; ++i) {
		int u, v;
		cin >> u >> v;
		insert(u, v);
	}
	memset(dfn, 0, sizeof(dfn));
	memset(sccno, 0, sizeof(sccno));
	times = scc_cnt = 0;
	for (int i = 1; i <= n; ++i) {
		if (!dfn[i]) {  // 每个点都要尝试 dfs 一次
			dfs(i);
		}
	}
	cout << scc_cnt << endl;
	for (int i = 1; i <= scc_cnt; ++i) {
		for (set<int>::iterator it = scc[i].begin(); it != scc[i].end(); ++it) {
			cout << (*it) << " ";
		}
		cout << endl;
	}
	return 0;
}
//----------------------------------------------------------------------------
//求强连通分量缩点
#include <iostream>
#include <stack>
#include <set>
#include <cstring>
using namespace std;
const int maxm = 1010;
const int maxn = 110;
struct edge {
	int v;
	int next;
} E[maxm];
int p[maxn], eid = 0;
void init() {
	memset(p, -1, sizeof(p));
	eid = 0;
}
void insert(int u, int v) {
	E[eid].v = v;
	E[eid].next = p[u];
	p[u] = eid++;
}
int times = 0;
int dfn[maxn], low[maxn];
int scc_cnt = 0;  // 强连通分量数量
int sccno[maxn];
set<int> scc[maxn];
stack<int> S;
void dfs (int u) {
	dfn[u] = low[u] = ++times;
	S.push(u);
	for (int i = p[u]; i != -1; i = E[i].next) {
		int v = E[i].v;
		if (dfn[v] == 0) {  // v 没有被访问过,u, v 是树边
			dfs(v);
			low[u] = min(low[u], low[v]);
		} else if (!sccno[v]) {  // 对于已经求出 scc 的点,直接删除
			low[u] = min(low[u], dfn[v]);
		}
	}
	if (low[u] == dfn[u]) {
		++scc_cnt;
		while (true) {
			int x = S.top();
			S.pop();
			sccno[x] = scc_cnt;
			scc[scc_cnt].insert(x);
			if (x == u) {
				break;
			}
		}
	}
}
edge new_E[maxm];
int new_p[maxn], new_eid = 0;
void new_init() {
	memset(new_p, -1, sizeof(new_p));
	new_eid = 0;
}
void new_insert(int u, int v) {
	new_E[new_eid].v = v;
	new_E[new_eid].next = new_p[u];
	new_p[u] = new_eid++;
}
int main() {
	init();
	int n, m;
	cin >> n >> m;
	for (int i = 0; i < m; ++i) {
		int u, v;
		cin >> u >> v;
		insert(u, v);
	}
	memset(dfn, 0, sizeof(dfn));
	times = scc_cnt = 0;
	for (int i = 1; i <= n; ++i) {
		if (!dfn[i]) {
			dfs(i);
		}
	}
	new_init();
	for (int u = 1; u <= n; ++u) {
		for (int i = p[u]; i != -1; i = E[i].next) {
			int v = E[i].v;
			if (sccno[u] != sccno[v]) {
				new_insert(sccno[u], sccno[v]);
			}
		}
	}
	return 0;
}
//----------------------------------------------------------------------------
//差分约束求解不等式最小解
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e3 + 9;
const int M = 1e4 + 9;

struct edge {
	int v, w, fail;
	edge() {}
	edge(int _v, int _w, int _fail) {
		v = _v;
		w = _w;
		fail = _fail;
	}
} e[M << 1];
int head[N], 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]);
	head[u] = len++;
}
void add2(int u, int v, int w) {
	add(u, v, w);
	add(v, u, w);
}
int n, m;
int dis[N], in[N];
bool vis[N];
bool spfa(int u) {
	memset(vis, false, sizeof(vis));
	vis[u] = true;
	memset(dis, -1, sizeof(dis));
	dis[u] = 0;
	memset(in, 0, sizeof in);
	in[u] = 1;
	queue<int> q;
	q.push(u);
	while (!q.empty()) {
		u = q.front();
		q.pop();
		vis[u] = false;
		for (int j = head[u]; ~j; j = e[j].fail) {
			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;
					++in[v];
					if (in[v] > n + 1) {
						return true;
					}
				}
			}
		}
	}
	return false;
}

int main() {
	init();
	int u, v, w, op;
	cin >> n >> m;
	while (m--) {
		cin >> op;
		cin >> u >> v >> w;
		if (op == 1) {
			add(u, v, -w);
		}
		else if (op == 2) {
			add(v, u, w);
		}
		else {
			add(u, v, -w);
			add(v, u, w);
		}
	}
	for (int i = 1; i <= n; ++i) {
		add(0, i, 0);
	}
	if (spfa(0)) {
		cout << "no" << endl;
	} else {
		for (int i = 1; i <= n; ++i) {
			cout << "x" << i << " = " << dis[i] << endl;
		}
	}
	return 0;
}
//----------------------------------------------------------------------------
//判断二分图
const int MAX_N = 100;  // 点的上限
const int MAX_M = 10000;  // 边的上限
struct edge {
	int v, next;
} e[MAX_M];
int p[MAX_N], eid;
void init() {
	memset(p, -1, sizeof(p));
	eid = 0;
}
void insert(int u, int v) {
	e[eid].v = v;
	e[eid].next = p[u];
	p[u] = eid++;
}
int n, m;  // n 表示点数, m 表示边数
int ans[MAX_N]; // 标记每个点被染的颜色
bool dfs(int u, int color) {
	ans[u] = color;
	for (int i = p[u]; i != -1; i = e[i].next) {
		int v = e[i].v;
		if (!ans[v]) {
			if (!dfs(v, 3 - color)) {
				return false;
			}
		} else if (ans[v] == color) {
			return false;
		}
	}
	return true;
}
bool judge_bipartite(int n) {
	for (int i = 0; i < n; i++) {
		if (!ans[i]) {
			if (!dfs(i)) {
				return false;
			}
		}
	}
  return true;
}
//----------------------------------------------------------------------------
//2-SAT 问题
const int MAX_N = 100;  // 点的上限
const int MAX_M = 10000;  // 边的上限
struct edge {
	int v, next;
} E[MAX_M];
int p[MAX_N * 2], eid;
void init() {
	memset(p, -1, sizeof(p));
	eid = 0;
}
void insert(int u, int v) {
	e[eid].v = v;
	e[eid].next = p[u];
	p[u] = eid++;
}
int n, m;  // n 表示点数, m 表示边数
bool selected[MAX_N * 2]; // 标记每个点对是否被选,每一点对为2*i和2*i+1
int S[MAX_N * 2]; // 标记此次选择经过的点
int c; // 此次选择的点数
bool dfs(int u) {
	if (selected[u ^ 1]) {
		return false;//和假设矛盾
	}
	if (selected[u]) {
		return true;//和假设一样
	}
	selected[u] = true;
	S[c++] = u;
	for (int i = p[u]; i != -1; i = E[i].next) {
		int v = E[i].v;
		if (!dfs(v)) {
			return false;
		}
	}
	return true;
}
bool Two_SAT(int n) {
	for(int i = 0; i < 2 * n; i += 2) {
		if (!selected[i] && !selected[i + 1]) {
			c = 0;
			if (!dfs(i)) {
				while (c > 0) {
					selected[S[--c]] = false; // 失败后沿路清空选择
				}
				if (!dfs(i + 1)) {
					return false;
				}
			}
		}
	}
	return true;
}

有那么亿点长……

手打不易,给个赞再走

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值