HNACM(八)D-引水工程

本文探讨了如何运用最小生成树算法解决D-引水工程问题,重点在于选择自建水库的最低费用策略,并处理权值相等的情况。同时,提出了将自建费用视为结点间费用的巧妙方法,该思路由@Dart贡献。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


最小生成树算法

首先找到一个自建水库最少费用的 然后以此为根本 找到与之相关的边
还要注意当权值相等时的处理方法

#include <bits/stdc++.h> 
#define N 310
#define ll long long
#define MAX 11111
using namespace std;
int n, w[N], p[N][N], water[N];
//w[N]是 每个地方自建水库所需的费用
//water[N]是存放已经解决用水问题的地方 
bool vis[N];
int prim(int pos, int sum){
    int i, j, k, cur = 0;
    memset(vis, false, sizeof(vis));
    memset(water, 0, sizeof(water));
    water[cur++] = pos;
    vis[pos] = true;
    while(cur < n){
        int mindist = MAX;
        int min1 = MAX;
        int u = -1;
        for (i = 0; i < cur; i++){//遍历所有的已经解决用水问题的点
            for (j = 0; j < n; j++){
                if (!vis[j] && j != water[i]){
                    if (mindist > p[water[i]][j]){ //未解决问的点,找出从其他地方引水的最小费用 
                        mindist = p[water[i]][j];
                        min1 = w[j];
                        u = j;
                    }else if (mindist == p[water[i]][j] && min1 > w[j]){//如果当前最小值和权值相等,比较自建费用 
                        min1 = w[j];
                        u = j;
                    }
                }
            }
        }
        vis[u] = true;
        water[cur++] = u;
        sum += min(min1, mindist);//去自建费用和从其他地方引水的费用的较小者 
    }
    return sum;
}
int main(){
#ifndef ONLINE_JUDGE
//  freopen("1.txt", "r", stdin);
//  freopen("d.in", "r", stdin);
//  freopen("d1.out", "w", stdout);
#endif  
    int i, j, k, K, Min, pos;
    scanf("%d", &K);
    while(K--){
        scanf("%d", &n);
        Min = MAX;
        for (i = 0; i < n; i++){
            scanf("%d", &w[i]);
            if (Min > w[i]){//先寻找自建水库的费用最小的地方 
                pos = i;
                Min = w[i];
            }
        }
        for (i = 0; i < n; i++){
            for (j = 0; j < n; j++){
                scanf("%d", &p[i][j]);
            }
        }
        printf("%d\n", prim(pos, Min));//先从自建水库最小的地方开始 
    }
    return 0;
}

另一种方法,把自建费用当成结点0到结点i的费用,然后用最下生成树算法

感谢@Dart 提供思路

#include <iostream>
#include <cstdio>
#define MAXN 310
using namespace std;
int g[MAXN][MAXN], dis[MAXN];
bool vis[MAXN];
const int INF = 0x3f3f3f3f;
int n;
int prim(int st){
    int i, j, k;
    for (i = 0; i <= n; i++){
        vis[i] = false;
        dis[i] = g[st][i];
    }
    dis[st] = 0;
    vis[st] = true;
    int sum = 0;
    for (i = 1; i <= n; i++){
        int Min = INF;
        int v = -1;
        for (j = 0; j <= n; j++){
            if (!vis[j] && Min > dis[j]){
                v = j;
                Min = dis[j];
            }
        }
        vis[v] = true;
        sum += Min;
        for (j = 0; j <= n; j++){
            if (!vis[j] && g[v][j] < dis[j]){
                dis[j] = g[v][j];
            }
        }
    }
    return sum;
}
int main(){
    int i, j, k, T;
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        for (i = 1; i <= n; i++){
            scanf("%d", &g[0][i]);
            g[i][0] = g[0][i];
        }
        for (i = 1; i <= n; i++){
            for (j = 1; j <= n; j++){
                scanf("%d", &g[i][j]);
            }
        }
        printf("%d\n", prim(0));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值