2.15总结

今天主要在写洛谷上的题,加上有几天没敲代码了,顺便复习了一些之前学过的东西

最小生成树(kruscal和prim)

kruscal算法要将所有的边从小到大进行排序,进行并查集的操作,将不是同一个父亲的合并,直到所有点为一个集合,合成了n-1条边(有n个点)

prim算法和dijstal算法有一些相似的地方,在所有点里任意找一个点,从这个点开始往下寻找,找到附件较小的权边为第二个点,再将两个点同时开始寻找,把相邻最小的权边确定为下一个点,以此操作,直到达到目的

P3366 【模板】最小生成树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz

输入格式

第一行包含两个整数 N,M,表示该图共有 N 个结点和 M 条无向边。

接下来 M 行每行包含三个整数 Xi​,Yi​,Zi​,表示有一条长度为 Zi​ 的无向边连接结点 Xi​,Yi​。

输出格式

如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz

输入输出样例

输入 

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

输出 

7

说明/提示

数据规模:

对于 20%20% 的数据,N≤5,M≤20。

对于 40%40% 的数据,N≤50,M≤2500。

对于 70%70% 的数据,N≤500,M≤104。

对于 100%100% 的数据:1≤N≤5000,1≤M≤2×105,1≤Zi​≤104。

思路:建立结构体将样例输入后可以使用kruscal来解决,将所有的数按权边大小从小到大进行排列(要求最小就要顺序排列),依次进行并查集的操作,每合并一次cnt+1、sum加上此时的权边值,当cnt的值等于点数-1时输出结果结束程序(因为是从小到大排列,得到的自然会是最小值),否则输出orz结束程序

代码:

#include<iostream>
#include<algorithm>
using namespace std;
int n, m,sum,counts;
int f[200005];
struct edge {
	int x;
	int y;
	int z;
}eg[200005];
bool cmp(edge a, edge b) {
	return a.z < b.z;
}
//void quicksort(int left, int right) {
//	int i, j;
//	i = left;
//	j = right;
//	struct edge t;
//	while (i != j) {
//		while (eg[j].z > eg[left].z && i < j)
//			j--;
//		while (eg[i].z < eg[left].z && i < j)
//			i++;
//		if (i < j) {
//			t = eg[i];
//			eg[i] = eg[j];
//			eg[j] = t;
//		}
//	}
//	t = eg[left];
//	eg[left] = eg[i];
//	eg[i] = eg[left];
//	quicksort(left, i - 1);
//	quicksort(i + 1, right);
//	return;
//}
int find(int x) {
	if (f[x] == x)return x;
	f[x] = find(f[x]);
	return f[x];
}
int merge(int x, int y) {
	int f1 = find(x);
	int f2 = find(y);
	if (f1 != f2) {
		f[f1] = f2;
		return 1;
	}
	return 0;
}
int main() {
	cin >> n >> m;
	sum = 0, counts = 0;
	for (int i = 1; i <= m; i++) {
		cin >> eg[i].x >> eg[i].y >> eg[i].z;
	}
	for (int i = 1; i <= m; i++)
		f[i] = i;
	/*quicksort(1, m);*/
	sort(eg + 1, eg + 1 + m, cmp);
	for (int i = 1; i <= m; i++) {
		if (merge(eg[i].x, eg[i].y)) {
			counts++;
			sum+=eg[i].z;
		}
		if (counts == n - 1) {
			cout << sum;
			return 0;
		}
	}
	cout << "orz" << endl;
	return 0;
}

P1194 买礼物 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

又到了一年一度的明明生日了,明明想要买 B 样东西,巧的是,这 B 样东西价格都是 A 元。

但是,商店老板说最近有促销活动,也就是:

如果你买了第 I 样东西,再买第 J 样,那么就可以只花 KI,J​ 元,更巧的是,KI,J​ 竟然等于 KJ,I​。

现在明明想知道,他最少要花多少钱。

输入格式

第一行两个整数,A,B。

接下来 B 行,每行B 个数,第 I 行第 J 个为 KI,J​。

我们保证 KI,J​=KJ,I​ 并且 KI,I​=0。

特别的,如果 KI,J​=0,那么表示这两样东西之间不会导致优惠。

注意 KI,J​ 可能大于 A。

输出格式

一个整数,为最小要花的钱数。

输入输出样例

输入 

1 1
0

输出 

1

输入 

3 3
0 2 4
2 0 2
4 2 0

输出 

7

说明/提示

样例解释 22。

先买第 22 样东西,花费 33 元,接下来因为优惠,买 1,31,3 样都只要 22 元,共 77 元。

(同时满足多个“优惠”的时候,聪明的明明当然不会选择用 44 元买剩下那件,而选择用 22 元。)

数据规模

对于 30%30% 的数据,1≤B≤10。

对于 100%100% 的数据,1≤B≤500,0≤A,KI,J​≤1000。

思路:利用买完I后买J这个规则进行构图,把Kij的价格看成权边,每一个Kij有着自己的邻点,同样使用kruscal算法先排序,然后进行并查集操作,当原始价格A小于Kij的价格时加上A,否则加上Kij

代码:

#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
	int x;
	int y;
	int w;
}edge[1000005];
int fa[1000005];
bool cmp(node x, node y)
{
	return x.w < y.w;
}
int find(int x)
{
	if (x == fa[x])return x;
	fa[x] = find(fa[x]);
	return fa[x];
}
void combine(int x, int y)
{
	x = find(x);
	y = find(y);
	fa[x] = y;
}
int main()
{
	int a, b;
	cin >> a >> b;
	int jj = 0;
	for (int i = 1; i <= b; i++)
		fa[i] = i;
	for (int i = 1; i <= b; i++)
		for (int j = 1; j <= b; j++)
		{
			cin >> edge[++jj].w;
			edge[jj].x = i;
			edge[jj].y = j;
			if (edge[jj].w == 0)edge[jj].w = a;
		}
	sort(edge + 1, edge + 1 + jj, cmp);
	int sum = a;
	for (int i = 1; i <= jj; i++)
	{
		if (find(edge[i].x) != find(edge[i].y)) {
			combine(edge[i].x, edge[i].y);
			if (edge[i].w < a)
				sum += edge[i].w;
			else sum += a;
		}
	}
	cout << sum << endl;
	return 0;
}

P1359 租用游艇 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

长江游艇俱乐部在长江上设置了 n 个游艇出租站1,2,⋯,n。游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。游艇出租站 i 到游艇出租站 j 之间的租金为 r(i,j)(1≤i<j≤n)。试设计一个算法,计算出从游艇出租站 11 到游艇出租站 n 所需的最少租金。

输入格式

第一行中有一个正整数 n,表示有 n 个游艇出租站。接下来的 n−1 行是一个半矩阵 r(i,j)(1≤i<j≤n)。

输出格式

输出计算出的从游艇出租站 11 到游艇出租站 n 所需的最少租金。

输入输出样例

输入 

3
5 15
7

输出 

12

说明/提示

n≤200,保证计算过程中任何时刻数值都不超过 106。

思路:把数组补全之后用floyed,用一个二维数组来得到每两个点之间的最短距离,最后直接输出a[起始点][目标点]

代码:

#include<iostream>
using namespace std;
int main() {
	int n;
	int a[205][205];
	cin >> n;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			a[i][j] = 999999;
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = i + 1; j <= n; j++) {
			cin >> a[i][j];
		}
	}
	for (int k = 1; k <= n; k++) {
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				if (a[i][j] > a[i][k] + a[k][j])
					a[i][j] = a[i][k] + a[k][j];
			}
		}
	}
	cout << a[1][n] << endl;
	return 0;
}

  • 21
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值