题集 (最短路 并查集扩展)

A Coloring Contention(Gym 102433C)

最短路 + 链式前向星存储

题目链接

题目意思是
使用能够使Bob变换最多次数的方法,求出Bob变换的次数,已知是最优,那么Bob第一步确定颜色,其余每一步必须改变颜色,所以两点间距离可以设置为1,求出Bob到达终点的距离然后减1就可以了,本题我用的链式前向星用来存储数据,跑完即可

#include <iostream>
#include <memory.h>
#include <queue>
using namespace std;
const int M = 100010;
const int INF = 0x3f3f3f3f;
struct node {
	int u, v, next1;
}edge[2 * M];
int head[M * 2], cnt, dis[M * 2];
bool vis[M];
int n, m;
void init() {
	for (int i = 1; i <= n; ++i) {
		head[i] = -1;
	}
	cnt = 0;
}
void addNode(int u, int v) {
	edge[cnt].u = u;
	edge[cnt].v = v;
	edge[cnt].next1 = head[u];
	head[u] = cnt++;
}

void spfa(int start1) {
	queue<int> que;
	dis[start1] = 0;
	vis[start1] = true;
	que.push(start1);
	while (!que.empty()) {
		int now1 = que.front();
		que.pop();
		if (now1 == n)
			return;
		for (int i = head[now1]; i != -1; i = edge[i].next1) {
			if (!vis[edge[i].v]) {
				dis[edge[i].v] = dis[edge[i].u] + 1;
				vis[edge[i].v] = 1;
				que.push(edge[i].v);
			}
		}
		//cout << "#";
	}
}

int main() {
	cin >> n >> m;
	init();
	int u, v, w;
	for (int i = 0; i < m; ++i) {
		cin >> u >> v;
		addNode(u, v);
		addNode(v, u);
	}
	spfa(1);
	cout << dis[n] - 1;
	return 0;
}


B Jzzhu and Cities(CodeForces 449B)

迪杰斯特拉 + 链式前向星存储

题目链接

本题是先输入一些不能被删除的道路,然后输入一些能够被删除的铁路,使用链式前向星存储道路,dijkstra跑完全程,在跑的时候记录下来每个节点的入度,跑完后,查看铁路,如果铁路长度大于到铁路终点的最短路,那么必不可能参与最短路的组成,最终值加1,如果铁路长度等于到铁路终点的最短路但是铁路终点的入度是1,那么也就说明,该铁路不能被删除,如果铁路终点大于1,则说明该点可以被删除

#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
#define ll  long long 
const ll M = 100010;
const ll INF = 0x3f3f3f3f;
struct Edge {
	ll u, v, w, next, flag;
}edge[M * 10];
ll head[M * 2], dis[M * 2], nodev[M], nodew[M];
ll n, m, k, cnt = 0;
int in[M * 2];
void addEdge(ll u, ll v, ll w, ll flag) {
	edge[cnt].u = u;
	edge[cnt].v = v;
	edge[cnt].w = w;
	edge[cnt].flag = flag;
	edge[cnt].next = head[u];
	head[u] = cnt++;
}
bool vis[M * 2];
bool vis1[M * 2];
struct node {
	ll H;
	ll u;
	ll flag;
	bool operator < (const node& x) const {
		return H > x.H;
	}
}start1, now;
void init() {
	for (ll i = 0; i <= n; ++i) {
		head[i] = -1;
		dis[i] = INF;
	}
}
ll ans = 0;
void dij() {
	priority_queue<node> que;
	start1.H = 0;
	start1.u = 1;
	que.push(start1);
	dis[start1.u] = 0;
	while (!que.empty()) {
		now = que.top();
		que.pop();
		if (vis[now.u]) continue;
		vis[now.u] = 1;
		//if (now.flag == 1) {
		//	ans++;
		//}
		for (ll i = head[now.u]; i != -1; i = edge[i].next) {
			if (dis[edge[i].v] > dis[edge[i].u] + edge[i].w && !vis[edge[i].v]) {
				dis[edge[i].v] = dis[edge[i].u] + edge[i].w;
				now.H = dis[edge[i].v];
				now.u = edge[i].v;
				now.flag = edge[i].flag;
				que.push(now);
				// 入度刷新
				in[edge[i].v] = 1;
				//if(now.u) 
			}
			else if (dis[edge[i].v] == dis[edge[i].u] + edge[i].w) {
				in[edge[i].v]++;
			}
		}
	}
}
int main() {

	scanf("%lld%lld%lld", &n, &m, &k);
	ll u, v, w;
	init();
	for (ll i = 0; i < m; ++i) {
		scanf("%lld%lld%lld", &u, &v, &w);
		addEdge(u, v, w, 0);
		addEdge(v, u, w, 0);
	}
	for (ll i = 0; i < k; ++i) {
		scanf("%lld%lld", &nodev[i], &nodew[i]);
		addEdge(1, nodev[i], nodew[i], 1);
		addEdge(nodev[i], 1, nodew[i], 1);
	}
	dij();
	for (int i = 0; i < k; ++i) {
		if (nodew[i] > dis[nodev[i]]) {
			ans++;
		}
		else if (nodew[i] == dis[nodev[i]] && in[nodev[i]] > 1) {
			ans++;
			in[nodev[i]]--;
		}
	}
	cout << ans << endl;
	return 0;
}


C Find them, Catch them(POJ 1703)

构造并查集

题目链接

本题有两种输入,一种是A用来询问两个罪犯的帮派,一种是D告诉我们两个罪犯不是同一个帮派。
那么我们可以将每个罪犯a设置为一个帮派每个罪犯a+n设置为另一个帮派,那么在每次询问时如果a和b帮派寻找父亲的时候是一样的说明为一个帮派,如果a和b+n帮派寻找父亲的时候是一样的说明不为一个帮派,剩下的一种情况就是还不确定帮派.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int M = 100010;

int pre[M * 2];
int n, m;

int find(int son) {
	/*路径压缩*/
	int deson, dad1;
	deson = son;
	while (son != pre[son]) son = pre[son]; // 直到找到爷爷节点
	// 一直更新到爷爷节点 期间每个点都更新成爷爷节点
	while (deson != pre[deson]) {
		dad1 = pre[deson];
		pre[deson] = son;
		deson = dad1;
	}
	return son;
}

void init(int n) {
	for (int i = 1; i <= 2 * n; ++i) {
		pre[i] = i;
	}
}

int main() {
	int t, dada1, dada2, dadb1, dadb2, a, b;
	char c;
	scanf("%d", &t);
	while (t--) {
		scanf("%d%d", &n, &m);
		init(n);
		for (int i = 0; i < m; ++i) {
			getchar();
			c = getchar();
			if (c == 'A') {
				scanf("%d%d", &a, &b);
				dada1 = find(a);
				dada2 = find(a + n);
				dadb1 = find(b);
				dadb2 = find(b + n);
				if (dada1 == dadb1) printf("In the same gang.\n"); // 一个帮派
				else if (dada1 == dadb2) printf("In different gangs.\n"); // 不同的帮派
				else printf("Not sure yet.\n"); // 还不确定
			}
			else if (c == 'D') {
				scanf("%d%d", &a, &b);
				dada1 = find(a);
				dada2 = find(a + n);
				dadb1 = find(b);
				dadb2 = find(b + n);
				if (dada1 != dadb2) pre[dada1] = dadb2; // 将a 和b + n 设置为同一个帮派
				if (dada2 != dadb1) pre[dada2] = dadb1; // 将a + n 和b设置为同一个帮派
			}
		}
	}
	return 0;
}

D Cube Stacking (POJ 1988)

带权并查集
题目链接

本题有两种操作,一种是’M’将第一个数所在的列的所有方块放到第二个的上面,另一种是’C’查询给出的数下面的方块数,这个题是个带权并查集,设置dis[i]用来保存i到i的父亲节点的距离,在路径压缩时将i的爷爷设置为i的父亲,并且更新dis[i],在每次查询时,用本列总共的块数减去查询点到父亲节点的距离,再减去自身,其中dis[x]是x到它父节点的距离也就是最高点的距离,因为已经压缩完毕后dis[x]的父节点就已经是最高点(爷爷)了,剩下的就是x下面的距离

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int M = 30030;

int pre[M], dis[M], num[M];

void init(int n) {
	for (int i = 1; i <= n; ++i) {
		pre[i] = i;
		num[i] = 1; // 初始化num[i] = 1
	}
}

int find(int son) {
	if (son == pre[son]) return son; // 设置出口 找到爷爷
	int tot = find(pre[son]); // 找到他的父亲
	dis[son] += dis[pre[son]]; // 路径压缩时改变当前son到上一级的距离 因为爸爸要变成爷爷 所以它当前的距离为累加过来的
	return pre[son] = tot; // 路径都压缩成爷爷
}

void merge(int x1, int x2) {
	int dad1 = find(x1);
	int dad2 = find(x2);
	if (dad1 == dad2) return; // 如果已经在同一行不进行移动
	/*把x1 移动到 x2*/
	pre[dad2] = dad1; 
	dis[dad2] += num[dad1];
	num[dad1] += num[dad2];
}

void query(int x) {
	int dad = find(x); // 已经路径压缩了 所以x父节点已经改变为爷爷 也就是最高点
	printf("%d\n", num[dad] - dis[x] - 1); // 本列总共的数量减去当前点到它父亲节点的距离 再减去自身 dis[x]是x到最高点的距离 因为已经压缩完毕他的父节点是最高点
}

int main() {
	int op, x1, x2;
	scanf("%d", &op);
	init(M - 1);
	char c;
	while (op--) {
		getchar();
		c = getchar();
		if (c == 'M') {
			scanf("%d%d", &x1, &x2);
			merge(x1, x2); // 把x1所在的放到x2
		}
		else if (c == 'C') {
			scanf("%d", &x1);
			query(x1);
		}
	}
	return 0;
}

posted on 2022-06-27 08:29  wxz0v0  阅读(21)  评论(0)    收藏  举报

导航