一般来说,能用prim做的题目,一般用kruskal也能做,而且复杂度更低,而kruskal能做的题prim有可能解决不了,但是在我今天做的题目发现其实在某些时刻还是应该选用prim而不是kruskal,题目如下
P1991 无线通讯网
国防部计划用无线网络连接若干个边防哨所。2 种不同的通讯技术用来搭建无线网络;
每个边防哨所都要配备无线电收发器;有一些哨所还可以增配卫星电话。
任意两个配备了一条卫星电话线路的哨所(两边都ᤕ有卫星电话)均可以通话,无论他们相距多远。而只通过无线电收发器通话的哨所之间的距离不能超过 D,这是受收发器的功率限制。收发器的功率越高,通话距离 D 会更远,但同时价格也会更贵。
收发器需要统一购买和安装,所以全部哨所只能选择安装一种型号的收发器。换句话说,每一对哨所之间的通话距离都是同一个 D。你的任务是确定收发器必须的最小通话距离 D,使得每一对哨所之间至少有一条通话路径(直接的或者间接的)。
输入输出格式
输入格式:
从 wireless.in 中输入数据第 1 行,2 个整数 S 和 P,S 表示可安装的卫星电话的哨所数,P 表示边防哨所的数量。接下里 P 行,每行两个整数 x,y 描述一个哨所的平面坐标(x, y),以 km 为单位。
输出格式:
输出 wireless.out 中
第 1 行,1 个实数 D,表示无线电收发器的最小传输距离,精确到小数点后两位。
输入输出样例
输入样例#1: 复制
2 4 0 100 0 300 0 600 150 750
输出样例#1: 复制
212.13
说明
对于 20% 的数据:P = 2,S = 1
对于另外 20% 的数据:P = 4,S = 2
对于 100% 的数据保证:1 ≤ S ≤ 100,S < P ≤ 500,0 ≤ x,y ≤ 10000。
// luogu-judger-enable-o2
#include<iostream>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
vector<double> v;
struct Node{
int x, y;
int cnt;
}node[510];
struct edge{
int u, v;
double cost;
}E[250010];
bool cmp(edge a, edge b) {
return a.cost < b.cost;
}
int father[510];
int findFather(int x) {
while (x != father[x]) {
x = father[x];
}
return x;
}
int kruskal(int n, int m) {
int Num_Edge = 0;
for (int i = 0; i < n; i++) {
father[i] = i;
}
sort(E, E + m, cmp);
for (int i = 0; i < m; i++) {
int faU = findFather(E[i].u);
int faV = findFather(E[i].v);
if (faU != faV) {
father[faU] = faV;
v.push_back(E[i].cost);
Num_Edge++;
if (Num_Edge == n - 1) break;
}
}
if (Num_Edge != n - 1) return -1;
else return 1;
}
int main() {
int s, p;
cin >> s >> p;
for (int i = 0; i < p; i++) {
cin >> node[i].x >> node[i].y;
node[i].cnt = i;
}
int cnt = 0;
for (int i = 0; i < p; i++) {
for (int j = i + 1; j < p; j++) {
double dis = sqrt(pow(node[i].x - node[j].x, 2) + pow(node[i].y - node[j].y, 2));
E[cnt].u = node[i].cnt;
E[cnt].v = node[j].cnt;
E[cnt++].cost = dis;
}
}
kruskal(p, cnt);
printf("%.2f", v[v.size() - s]);
return 0;
}
思考:关于这道题目其实争议挺多的,有人说要删除边啥的,有人是用瓶颈生成树来做的,不过我感觉我的这种思路算是比较大众的思路,就是一个kruskal一套下来,然后贪心从后往前取就可以了,在这里我使用了结构体来处理坐标的问题,直接把他转换成了一个结点,然后按照常规的套路来便可以了,然而接下来看下一道题目
P1265 公路修建
某国有n个城市,它们互相之间没有公路相通,因此交通十分不便。为解决这一“行路难”的问题,政府决定修建公路。修建公路的任务由各城市共同完成。
修建工程分若干轮完成。在每一轮中,每个城市选择一个与它最近的城市,申请修建通往该城市的公路。政府负责审批这些申请以决定是否同意修建。
政府审批的规则如下:
(1)如果两个或以上城市申请修建同一条公路,则让它们共同修建;
(2)如果三个或以上的城市申请修建的公路成环。如下图,A申请修建公路AB,B申请修建公路BC,C申请修建公路CA。则政府将否决其中最短的一条公路的修建申请;
(3)其他情况的申请一律同意。
一轮修建结束后,可能会有若干城市可以通过公路直接或间接相连。这些可以互相:连通的城市即组成“城市联盟”。在下一轮修建中,每个“城市联盟”将被看作一个城市,发挥一个城市的作用。
当所有城市被组合成一个“城市联盟”时,修建工程也就完成了。
你的任务是根据城市的分布和前面讲到的规则,计算出将要修建的公路总长度。
输入输出格式
输入格式:
第一行一个整数n,表示城市的数量。(n≤5000)
以下n行,每行两个整数x和y,表示一个城市的坐标。(-1000000≤x,y≤1000000)
输出格式:
一个实数,四舍五入保留两位小数,表示公路总长。(保证有惟一解)
输入输出样例
输入样例#1: 复制
4 0 0 1 2 -1 2 0 4
输出样例#1: 复制
6.47
说明
修建的公路如图所示:
思考:看到这个题目是不是感觉特别熟悉有木有,感觉就是和上面那个题目一模一样,就是换了个法子问你,然后我就把上面的代码稍微做了修改,然后把样例输了一遍,过了,然后我信心满满的交了上去,有俩个测试点内存不够,然后我就开始吧int改成long long各种改,大概过了30分钟还是没有头绪,后来看了别人的博客才知道如果矩阵开到5000*5000是会内存超限的,我竟然糊涂到把这个都忘了,那么问题来了,接下来要怎么修改,这个时候我们仔细看一下题目就会发现,这是一个稠密图,也就是点少边多,完全适用于prim算法,而且还可以直接用矩阵来保存点的值,接下来直接上代码
// luogu-judger-enable-o2
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXV = 5010;
int p[MAXV][2];
const int INF = 1e9;
double d[MAXV];
bool vis[MAXV];
double dist(int x1,int y1,int x2,int y2)
{
return sqrt((double)(x1-x2)*(x1-x2)+(double)(y1-y2)*(y1-y2));
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> p[i][0] >> p[i][1];
}
double ans = 0;
fill(d, d + MAXV, INF);
d[0] = 0;
for (int i = 0; i < n; i++) {
int u = -1;
double Min = INF;
for (int j = 0; j < n; j++) {
if (d[j] < Min && vis[j] == false) {
Min = d[j];
u = j;
}
}
vis[u] = true;
ans += d[u];
for (int v = 0; v < n; v++) {
double t=dist(p[u][0],p[u][1],p[v][0],p[v][1]);
if (t < d[v]){
d[v] = t;
}
}
}
printf("%.2f", ans);
return 0;
}
那么问题又来了,既然这个题目可以用prim,那么上一个题应该也是可以用prim来做的,事实上就是如此,这个题目真是一个比较好的教训,以后还是要根据实际情况来使用相对应的算法