Prim算法模版&&Kruskal算法模板

(有任何问题欢迎留言或私聊

Prim算法

POJ1258题目链接:传送门
prim算法的过程:
这里写图片描述
首先我用到的几个变量

int ar[N][N;//存图,ar[i][j]=x,表示i和j的距离,则ar[i][i]=0;
int dis[N];//dis[i]存的是起始点1形成的生成树中,点1到点i 还需要距离!
int vis[N];//表示i节点是否在生成树中

以a点为根节点,dis[i]存的是起始点1形成的生成树中,点1到点i 还需要的距离

  • 1.刚开始a只和b和d相连,所以dis[b]=dis[2]=4,dis[h]=dis[8]=8。其他点还没有和a相连,所以dis[i]=INF无穷大。

  • 2.选择dis最小的,就是dis[2],值为4,下标为2.然后把b加入生成树中。

  • 3.因为b加入了a的生成树中,所以和b相连的点也就和a相连了。这时候来更新他们和a点的距离。

  • 4.因为ar[b][c]=8,ar[b][h]=11,而dis[c]=INF,dis[h]=8。选择较小的值。所以,更新后,dis[c]=8,dis[h]还是8.

  • 5.求解过程以此类推。因为一颗n个节点的树只有n-1条边,所以这个过程只执行n-1次。

  • 6.对了,要注意,已经加入生成树的节点不要更新也不用询问是否加入生成树中。所以我用到了vis数组。vis[i]=1表示在生成树中,vis[i]=0表示不在生成树中。

  • 7.还有就是如果在求解过程中,得到的最小dis为INF,表明不存在最小生成树
    推荐视频讲解:
    qsc学姐 西工大某大佬

AC代码:
#include<iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 105;
int n, ans;
int ar[N][N;//存图,ar[i][j]=x,表示i和j的距离,则ar[i][i]=0;
int dis[N];//dis[i]存的是起始点1形成的生成树中,点1到点i 还需要的距离
int vis[N];//表示i节点是否在生成树中
void init() {//这里是初始化
    ans = 0;
    memset(ar, 0, sizeof(ar));
    memset(dis, 0x3f, sizeof(dis));
}
void Prim() {
    memset(vis, 0, sizeof(vis));
    for (int i = 1; i <= n;++i) {
        dis[i] = ar[1][i];
    }
    vis[1] = 1;//起点肯定在生成树中
    for (int i = 1; i <= n; ++i) {
        int L = INF, m = -1;//要找到最小的dis,所以L初始赋值为无穷大,来逐步找到最小的dis,m表示下标
        for (int j = 1; j <= n; ++j) {//算法 进行n-1次
            if (dis[j] < L&&vis[j] == 0) {//找不在生成树中的节点
                L = dis[j];
                m = j;
            }
        }
        if (m == -1) break;
        ans += L;
        vis[m] = 1;//标记
        for (int j = 1; j <= n; ++j) {
            if (ar[m][j] < dis[j]&&vis[j] == 0) {//更新
                dis[j] = ar[m][j];
            }
        }
    }
    printf("%d\n", ans);//最小生成树的权值
}
int main() {
    while (~scanf("%d", &n)) {
        init();
        for(int i = 1; i <= n; ++i){
            for(int j = 1; j <= n; ++j){
                scanf("%d", &ar[i][j]);
            }
        }
        Prim();
    }
    return 0;
}

Kruskal算法

POJ1861题目链接:传送门

kruskal上面的视频中有讲解。要用到并查集这个算法。
并查集qsc学姐也有讲,看他的视频就好了,讲的非常棒。

AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define iis std::ios::sync_with_stdio(false)
using namespace std;
typedef long long LL;
const int N = 1005;
const int INF = 0x3f3f3f3f;
int n, tot, ans, m, num;
int fa[N];
int Num[15005];
struct lp{//邻接表存图
    int x,y;//x,y表示边的两个端点
    int len;//边权值
    friend bool operator<(const lp &a,const lp &b){//重载<,这样就可以用sort排序
        return a.len<b.len;
    }
}cw[15005];
inline void init(){
    tot = 2;ans = num = 0;
    for(int i = 0; i <= n; ++i) fa[i] = i;//并查集,初始每个节点的fa是他们自己
}
inline int Fi(int x){//递归求解每个点的根节点
    return fa[x]==x? x: fa[x] = Fi(fa[x]);
}
inline int unsame(int x, int y){//x,y是否是联通
    int p = Fi(x), q = Fi(y);
    return p != q;
}
inline void Union(int x, int y){//把x和y联通
    int p = Fi(x), q = Fi(y);
    if(p == q)return;
    fa[q] = p;
    return;
}
void kru(){
    sort(cw, cw + m);//排序,边权小的排在前面,其实就是贪心
    int cnt = 0;//加入生成树中的边
    for(int i = 0; i < m; ++i){
        if(unsame(cw[i].x, cw[i].y)){//如果x和y不连通,就把这条边加入生成树中
	        cnt++;
            Union(cw[i].x, cw[i].y);
            Num[num++] = i;
            ans = cw[i].len;
        }
        if(cnt == n-1){//一棵树有n-1条边
            return;
        }
    }
}
int main(){
    while(~scanf("%d%d", &n, &m)){
        init();
        for(int i = 0, x, y, z; i < m; ++i) {
            scanf("%d%d%d",&x,&y,&z);
            cw[i].x = x;
            cw[i].y = y;
            cw[i].len = z;
        }
        kru();
        printf("%d\n%d\n", ans, num);
        for(int i = 0; i < num; ++i){
            int t = Num[i];
            printf("%d %d\n", cw[t].x, cw[t].y);
        }
    }
    return 0;
}

POJ1798-Truck History

题目链接:传送门

AC代码:
#include<iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int N = 2005;
int n, ans;
char s[N][N];
int dis[N],ar[N][N];
int vis[N];
void Prim() {
    ans = 0;
    memset(vis, 0, sizeof(vis));
    for (int i = 1; i <= n;++i) {
        dis[i] = ar[1][i];
    }
    vis[1] = 1;
    for (int i = 1; i < n; ++i) {
        int L = INF,m = -1;
        for (int j = 1; j <= n; ++j) {
            if (dis[j] && dis[j] < L&&vis[j]==0) {
                L = dis[j];
                m = j;
            }
        }
        if(m == -1) break;
        ans += L;
        vis[m] = 1;
        for (int j = 1; j <= n; ++j) {
            if (ar[m][j] < dis[j]&&vis[j] == 0) {
                dis[j] = ar[m][j];
            }
        }
    }
    printf("The highest possible quality is 1/%d.\n", ans);
}
int getval(int x, int y){
    int an = 0;
    for(int i = 0; i <= 6; ++i){
        if(s[x][i] != s[y][i])an++;
    }
    return an;
}
int main() {
    while (~scanf("%d", &n)&&n) {
        for(int i = 1; i <= n; ++i){
            scanf("%s",s[i]);
        }
        for(int i = 1; i <= n; ++i) {
            for(int j = 1;j <= n; ++j){
                if(i == j) ar[i][j] = 0;
                else ar[i][j] = getval(i, j);
            }
        }
        Prim();
    }
    return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值