最小生成树+最短路径作业

目录

1.P3366 【模板】最小生成树

1.题目

2.思路

3.代码

2.P1991 无线通讯网

1.题目

2.思路

3.代码

3.P2121 拆地毯

1.题目

2.思路

3.代码

4.P1396 营救

1.题目

2.思路

3.代码

5.P1194 买礼物

1.题目

2.思路

3.代码

6.P2872 [USACO07DEC] Building Roads S

1.题目

2.思路

3.代码

7.P1359 租用游艇

1.题目

2.思路

3.代码


1.P3366 【模板】最小生成树

1.题目

2.思路

就是最小生成树的模板,不过对于Prim 算法,要记得去重边,这是一个坑

3.代码

kruskal 算法

#include <stdio.h>
struct edge
{
	int u;
	int v;
	int w;
};
struct edge e[200010];
int n, m;
int s[5100];
int count = 0;
int sum = 0;
void quick(int left, int right)
{
	if (left > right)
	{
		return;
	}
	int i = left;
	int j = right;
	struct edge t;
	int temp = e[left].w;
	while (i != j)
	{
		while (e[j].w >= temp && i < j)
		{
			j--;
		}
		while (e[i].w <= temp && i < j)
		{
			i++;
		}
		if (i < j)
		{
			t = e[i];
			e[i] = e[j];
			e[j] = t;
		}
	}
	t = e[left];
	e[left] = e[i];
	e[i] = t;
	quick(left, i - 1);
	quick(i + 1, right);
	return;
}
int Find(int x)
{
	if (s[x] == x)
	{
		return x;
	}
	else
	{
		return s[x] = Find(s[x]);
	}
}
int main()
{
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		scanf("%d %d %d", & e[i].u, &e[i].v, &e[i].w);
	}
	quick(1, m);
	for (int i = 1; i <= n; i++)
	{
		s[i] = i;
	}
	for (int i = 1; i <= m; i++)
	{
		int t1 = Find(e[i].u);
		int t2 = Find(e[i].v);
		if (t1 != t2)
		{
			s[t2] = t1;
			count++;
			sum += e[i].w;
		}
		if (count == n - 1)
		{
			break;
		}
	}
	if (count != n - 1)
	{
		printf("orz\n");
	}
	if (count == n - 1)
	{
		printf("%d\n", sum);
	}
	return 0;
}

Prim 算法

#include <stdio.h>
int e[5010][5010];
int book[5010] = { 0 };
int dis[5010];

int main()
{
	int n, m;
	int count = 0;
	int sum = 0;
	int inf = 99999999;//表示无穷
	scanf("%d %d",&n, &m);
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (i == j)
			{
				e[i][j] = 0;
			}
			else
			{
				e[i][j] = inf;
			}
		}
	}
	for (int i = 1; i <= m; i++)//需要注意重边的情况,两个顶点之间可能有多条边,取最小的那条边存入邻接矩阵;
	{
		int t1, t2, t3;
		scanf("%d %d %d", &t1, &t2, &t3);
		if (t3 < e[t1][t2])//改进
		{
			e[t1][t2] = t3;
			e[t2][t1] = t3;
		}
	}
	for (int i = 1; i <= n; i++)
	{
		dis[i] = e[1][i];
	}
	book[1] = 1;
	count++;
	int j;
	for (int i = 1; i <= n - 1; i++)
	{
		int min = inf;
		for (int i = 1; i <= n; i++)
		{
			if (book[i] == 0 && dis[i] < min)
			{
				min = dis[i];
				j = i;
			}
		}
		if (min != inf)//如果min==inf 则没有找到最小的边,该点到达不了那几个顶点,//改进
		{
			book[j] = 1;
			count++;
			sum += dis[j];
		}
		for (int k = 1; k <= n; k++)
		{
			if (book[k] == 0 && dis[k] > e[j][k])
			{
				dis[k] = e[j][k];
			}
		}
	}
	if (count == n)
	{
		printf("%d\n", sum);
	}
	if (count != n)
	{
		printf("orz\n");
	}
	return 0;
}

2.P1991 无线通讯网

1.题目

2.思路

将点的坐标转化为点到点的距离。然后再套用kruskal 算法的模板;连通的边的数量只需要为P-S。就可以打印出D了。因为根据题目意思求的是用无线电的距离;减去用卫星电话的边;再求存入所有的边的最大值;

3.代码

#include <stdio.h>
#include <math.h>
struct edge
{
	int u;
	int v;
	double w;
}e[250010];
int s[510];
int s1, p;
int a[510], b[510];
void quick(int left, int right)
{
	if (left > right)
	{
		return;
	}
	int i = left;
	int j = right;
	struct edge t;
	while (i != j)
	{
		while (e[j].w >= e[left].w && i < j)
		{
			j--;
		}
		while (e[i].w <= e[left].w && i < j)
		{
			i++;
		}
		if (i < j)
		{
			t = e[i];
			e[i] = e[j];
			e[j] = t;
		}
	}
	t = e[left];
	e[left] = e[i];
	e[i] = t;
	quick(left, i - 1);
	quick(i + 1, right);
}
int Find(int x)
{
	if (s[x] == x)
	{
		return x;
	}
	else
	{
		return s[x] = Find(s[x]);
	}
}
int main()
{
	scanf("%d %d", &s1, &p);
	for (int i = 1; i <= p; i++)
	{
		scanf("%d %d", &a[i], &b[i]);
	}
	int n = 0;
	for (int i = 1; i <= p; i++)
	{
		for (int j = 1; j <= p; j++)
		{
			n++;
			e[n].w = sqrt((a[j] - a[i]) * (a[j] - a[i]) + (b[j] - b[i]) * (b[j] - b[i]));//将坐标转换成顶点到顶点的边;要用n来另外记录边数。
			e[n].u = i;
			e[n].v = j;
		}
	}
	for (int i = 1; i <= p; i++)
	{
		s[i] = i;
	}
	quick(1, n);
	double ans = 0;
	int count = 0;
	for (int i = 1; i <= n; i++)
	{
		int t1 = Find(e[i].u);
		int t2 = Find(e[i].v);
		if (t1 != t2)
		{
			count++;
			s[t2] = t1;
			ans = e[i].w;
		}
		if (count == p - s1)
		{
			break;
		}
	}
	printf("%.2lf", ans);
	return 0;
}

3.P2121 拆地毯

1.题目

2.思路

直接套用最小生成树的模板,不过题目要我们求的是最大值,不就是用最小生成树的模板求最大生成树吗?

3.代码

#include <stdio.h>
struct edge
{
	int u;
	int v;
	int w;
};
struct edge e[100010];
int s[100010];
int n, m, k;
int count = 0;
int sum = 0;
void quick(int left, int right)
{
	if (left > right)
	{
		return;
	}
	int i = left;
	int j = right;
	struct edge t;
	while (i != j)
	{
		while (e[j].w <= e[left].w && i < j)
		{
			j--;
		}
		while (e[i].w >= e[left].w && i < j)
		{
			i++;
		}
		if (i < j)
		{
			t = e[i];
			e[i] = e[j];
			e[j] = t;
		}
	}
	t = e[left];
	e[left] = e[i];
	e[i] = t;
	quick(left, i - 1);
	quick(i + 1, right);
}
int Find(int x)
{
	if (s[x] == x)
	{
		return x;
	}
	else
	{
		return s[x] = Find(s[x]);
	}
}
int main()
{
	scanf("%d %d %d", &n, &m, &k);
	for (int i = 1; i <= m; i++)
	{
		scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].w);
	}
	for (int i = 1; i <= n; i++)
	{
		s[i] = i;
	}
	quick(1, m);
	for (int i = 1; i <= m; i++)
	{
		int t1 = Find(e[i].u);
		int t2 = Find(e[i].v);
		if (t1 != t2)
		{
			s[t2] = t1;//不要忘记合并了;
			count++;
			sum += e[i].w;
		}
		if (count == k)
		{
			break;
		}
	}
	printf("%d\n", sum);
	return 0;
}

4.P1396 营救

1.题目

2.思路

用kruskal 算法的模板将边从小到大排序,将边连通,直到1与3连通时,打印此时的边;此时就是最大的拥挤度了

3.代码

#include <stdio.h>
struct edge
{
	int u;
	int v;
	int w;
};
struct edge e[20010];
int s[10100];
int n, m, s1, t;
void quick(int left, int right)
{
	if (left > right)
	{
		return;
	}
	int i = left;
	int j = right;
	struct edge t;
	while (i != j)
	{
		while (e[j].w >= e[left].w && i < j)
		{
			j--;
		}
		while (e[i].w <= e[left].w && i < j)
		{
			i++;
		}
		if (i < j)
		{
			t = e[i];
			e[i] = e[j];
			e[j] = t;
		}
	}
	t = e[left];
	e[left] = e[i];
	e[i] = t;
	quick(left, i - 1);
	quick(i + 1, right);
}
int Find(int x)
{
	if (s[x] == x)
	{
		return x;
	}
	else
	{
		return s[x] = Find(s[x]);
	}
}
int main()
{
	scanf("%d %d %d %d", &n, &m, &s, &t);
	for (int i = 1; i <= m; i++)
	{
		scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].w);
	}
	for (int i = 1; i <= n; i++)
	{
		s[i] = i;
	}
	quick(1, m);
	for (int i = 1; i <= m; i++)
	{
		int a = Find(e[i].u);
		int b = Find(e[i].v);
		if (a != b)
		{
			s[a] = b;
		}
		if (Find(s1) == Find(t))
		{
			printf("%d\n", e[i].w);
			break;
		}
	}
	return 0;
}

5.P1194 买礼物

1.题目

2.思路

套用kruskal 的模板,将输入的数据转化成顶点到顶点的边值来记录;将边值从小到大排序;再遍历存储的边,不连通的,则加入树,连通且个数count++;当边的值与 a 值的较小值。最后结果打印sum+(B-count)*a 的值,因为可能题目给的数据有些边未连通;

3.代码

#include <stdio.h>
struct edge
{
	int u;
	int v;
	int w;
};
struct edge e[2500000];
int s[5010];
int count = 0;
int sum = 0;
int a, b;
void quick(int left, int right)
{
	if (left > right)
	{
		return;
	}
	int i = left;
	int j = right;
	struct edge t;
	while (i != j)
	{
		while (e[j].w >= e[left].w && i < j)
		{
			j--;
		}
		while (e[i].w <= e[left].w && i < j)
		{
			i++;
		}
		if (i < j)
		{
			t = e[i];
			e[i] = e[j];
			e[j] = t;
		}
	}
	t = e[left];
	e[left] = e[i];
	e[i] = t;
	quick(left, i - 1);
	quick(i + 1, right);
}
int Find(int x)
{
	if (s[x] == x)
	{
		return x;
	}
	else
	{
		return s[x] = Find(s[x]);
	}
}
int main()
{
	scanf("%d %d", &a, &b);
	int n = 0;
	for (int i = 1; i <= b; i++)
	{
		for (int j = 1; j <= b; j++)
		{
			int t = 0;
			scanf("%d", &t);
			if (j > i && t != 0)
			{
				n++;
				e[n].u = i;
				e[n].v = j;
				e[n].w = t;
			}
		}
	}
	for (int i = 1; i <= b; i++)
	{
		s[i] = i;
	}
	quick(1, n);
	for (int i = 1; i <= n; i++)
	{
		int t1 = Find(e[i].u);
		int t2 = Find(e[i].v);
		if (t1 != t2)
		{
			s[t1] = t2;
			count++;
			if (a > e[i].w)
			{
				sum += e[i].w;
			}
			else
			{
				sum += a;
			}
		}
		if (count == b - 1)
		{
			break;
		}
	}
	printf("%d\n", sum + (b - count) * a);//加上未链接的顶点;
	return 0;
}

6.P2872 [USACO07DEC] Building Roads S

1.题目

2.思路

用kruskal 算法的模板,不过有些区别,需要将m条边提前连通,表示已经加入的边;将点转换成坐标时,先记录边值的平方值;降低时间复杂度。

3.代码

#include <stdio.h>
#include <math.h>
struct edge
{
	int u;
	int v;
	double w;
};
struct edge e[5000100];
int s[10100];
int n, m;
double a[10100], b[10100];
int count = 0;
double sum = 0;
void quick(int left, int right)
{
	if (left > right)
	{
		return;
	}
	int i = left;
	int j = right;
	struct edge t;
	while (i != j)
	{
		while (e[j].w >= e[left].w && i < j)
		{
			j--;
		}
		while (e[i].w <= e[left].w && i < j)
		{
			i++;
		}
		if (i < j)
		{
			t = e[i];
			e[i] = e[j];
			e[j] = t;
		}
	}
	t = e[left];
	e[left] = e[i];
	e[i] = t;
	quick(left, i - 1);
	quick(i + 1, right);
}
int Find(int x)
{
	if (s[x] == x)
	{
		return x;
	}
	else
	{
		return s[x] = Find(s[x]);
	}
}
int main()
{
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
	{
		scanf("%lf %lf", &a[i], &b[i]);
	}
	int n1 = 0;
	for (int i = 1; i <= n; i++)
	{
		for (int j = i + 1; j <= n; j++)
		{
			n1++;//先用平方记录,降低时间复杂度,主要是这里卡不过
			e[n1].w = (a[j] - a[i]) * (a[j] - a[i]) + (b[j] - b[i]) * (b[j] - b[i]);//将坐标转换成顶点到顶点的边;要用n来另外记录边数。
			e[n1].u = i;
			e[n1].v = j;
		}
	}
	for (int i = 1; i <= n; i++)
	{
		s[i] = i;
	}
	for (int i = 1; i <= m; i++)//提前连通一些边
	{
		int x, y;
		scanf("%d %d", &x, &y);
		int t1 = Find(x);
		int t2 = Find(y);//提前连通边;
		if (t1 != t2)
		{
			count++;
			s[t2] = t1;
		}
	}
	quick(1, n1);
	for (int i = 1; i <= n1; i++)
	{
		int t1 = Find(e[i].u);
		int t2 = Find(e[i].v);
		if (t1 != t2)
		{
			count++;
			s[t2] = t1;
			sum += sqrt(e[i].w);
		}
		if (count == n - 1)
		{
			break;
		}
	}
	printf("%.2lf", sum);
	return 0;
}

7.P1359 租用游艇

1.题目

2.思路

求单源最短路径问题;因为是带权图,所以考虑用迪杰斯特拉算法;最后打印1到n的最短距离;

3.代码

#include <stdio.h>//求的是1到n的距离,最短单源路径;dj,带权图;
int e[210][210];
int dis[210];
int book[210] = { 0 };
int n;
int inf = 1000100;

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (i == j)
			{
				e[i][j] = 0;
			}
			else
			{
				e[i][j] = inf;
			}
		}
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = i + 1; j <= n; j++)
		{
			int a = 0;
			scanf("%d", &a);
			e[i][j] = a;
		}
	}
	for (int i = 1; i <= n; i++)
	{
		dis[i] = e[1][i];
	}
	book[1] = 1;
	int j;
	for (int i = 1; i < n; i++)
	{
		int min = inf;
		for (int h = 1; h <= n; h++)
		{
			if (book[h] == 0 && dis[h] < min)
			{
				min = dis[h];
				j = h;
			}
		}
		if (min != inf)
		{
			book[j] = 1;
			for (int k = 1; k <= n; k++)
			{
				if (book[k] == 0 && dis[k] > dis[j] + e[j][k])
				{
					dis[k] = dis[j] + e[j][k];
				}
			}
		}
	}
	printf("%d\n", dis[n]);
	return 0;
}

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

エース和

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值