【图论】POJ-1789 Truck History

版权声明:Johnson https://blog.csdn.net/m0_38055352/article/details/88941176

这段时间要沉迷刷题一段时间了,就让CSDN陪我一起吧!

一、题目大意

Advanced Cargo Movement,Ltd使用不同类型的卡车。有些卡车用于蔬菜运输,其他用于运输家具或砖块。该公司有自己的编码描述每种类型的卡车。代码只是一个正好七个小写字母的字符串(每个位置上的每个字母都有一个非常特殊的含义,但这对于此任务来说并不重要)。在公司历史的开始,只使用了一种卡车类型,但后来其他类型都是从它衍生出来的,然后从新类型中衍生出另一种类型,依此类推。

如今,ACM的财富足以支撑请历史学家来研究他们的历史。历史学家试图找出的一件事是所谓的推导计划,即卡车类型是如何推导的。他们将卡车类型的距离定义为卡车类型代码中具有不同字母的位置数。他们还假设每种卡车类型都来自其他卡车类型(第一种卡车类型除外,这种类型不是来自任何其他类型)。然后将推导计划的质量定义为:
                                               在这里插入图片描述
总和遍历推导计划中的所有类型对,使得to为原始类型,td为从其派生的类型,d(to,td)是类型的距离。由于历史学家失败了,你要编写一个程序来帮助他们。根据卡车类型的代码,您的程序应该找到最高质量的派生计划。

输入第一行是一个整数N,表示具有的卡车类型总数。然后N行,每一行是一个卡车类型代码,代码均为7位。

要求输出最高质量的派生计划。

二、题目思路以及AC代码

题目还是有点长的,但总的来说就是要给你一堆代码,然后找一条派生路径,让其派生质量达到最高,派生质量的定义如上所述。

再进行一步抽象,就是给你N个顶点,让你找寻一条路径,可以把所有的顶点连通,并且保证其代价之和最小。这么一说就比较明白了,最小生成树嘛。但在这里需要考虑的问题就是代价是什么?用Prim还是Kruskal?

在上述已经提到,每进行一次派生,就会花费的Distance,所以自然而然,如果卡车 i 派生出 j,那么就会有一条从 i 指向 j的一条边,其权值就是代价,也就是Distance,卡车类型代码中具有不同字母的位置数。

代价明确了,接下来考虑使用Prim还是Kruskal,当然你随便用一个也说不定可以,但是既然是学习,就尽量找最优的嘛,看看这个我们构造的图,由于每一个代码都可能派生出另一些代码,所以这个图是一个完全图。稀疏图用Kruskal,稠密图用Prim,ok,这题就是Prim了!

分析完毕,下面给出AC代码:

Prim:

扫描二维码关注公众号,回复: 5732039 查看本文章
#include <iostream>
#include <string>
#include <cstring>
#define MAXN 2020
#define INF 10000000
using namespace std;

int N;
int edges[MAXN][MAXN];
int dist[MAXN];
int vis[MAXN];
char trucks[MAXN][10];

void init() {
	for (int i = 0; i < MAXN; i++) {
		for (int j = 0; j < MAXN; j++) {
			if (i == j) edges[i][j] = 0;
			else edges[i][j] = INF;
		}
		dist[i] = INF;
		vis[i] = false;
	}
}

int Distance(char* a, char* b) {
	int cnt = 0;
	for (int i = 0; i < 7; i++) {
		if (a[i] != b[i]) {
			cnt++;
		}
	}
	return cnt;
}

int Prim() {
	for (int i = 0; i < MAXN; i++) {
		dist[i] = INF;
		vis[i] = false;
	}

	dist[1] = 0;
	int ans = 0;
	for (int i = 1; i <= N; i++) {
		int min = INF;
		int x = -1;

		for (int j = 1; j <= N; j++) {
			if (!vis[j] && min > dist[j]) {
				min = dist[x = j];
			}
		}

		if (x == -1) break;
		vis[x] = true;
		ans += min;

		for (int j = 1; j <= N; j++) {
			if (!vis[j] && dist[j] > edges[x][j]) {
				dist[j] = edges[x][j];
			}
		}
	}

	return ans;
}

int main()
{
	while (scanf("%d", &N) != EOF) {
		if (!N) break;
		init();

		for (int i = 1; i <= N; i++) {
			scanf("%s", &trucks[i]);
		}

		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {
				edges[i][j] = edges[j][i] = Distance(trucks[i], trucks[j]);
			}
		}

		printf("The highest possible quality is 1/%d.\n", Prim());
	}

    return 0;
}

这里需要注意的是:
千万不要用string输入字符串!!
千万不要用string输入字符串!!
千万不要用string输入字符串!!
在循环的情况下,用string输入的话会比char输入慢许多,亲身尝试,就以本题为例,用string,2000ms超时,用char,600ms

如有问题,欢迎大家指正!

猜你喜欢

转载自blog.csdn.net/m0_38055352/article/details/88941176