关于kruskal与prim算法在题目中的运用

一般来说,能用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 为单位。

输出格式:

扫描二维码关注公众号,回复: 4490978 查看本文章

输出 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来做的,事实上就是如此,这个题目真是一个比较好的教训,以后还是要根据实际情况来使用相对应的算法

猜你喜欢

转载自blog.csdn.net/LanQiLi/article/details/84781651