AcWing 1146. 新的開始(最小生成樹)

題幹:

發展採礦業當然首先得有礦井,小 F 花了上次探險獲得的千分之一的財富請人在島上挖了 n 口礦井,但他似乎忘記了考慮礦井供電問題。
爲了保證電力的供應,小 F 想到了兩種辦法:
(1)在礦井 i 上建立一個發電站,費用爲 vi(發電站的輸出功率可以供給任意多個礦井)。
(2)將這口礦井 i 與另外的已經有電力供應的礦井 j 之間建立電網,費用爲 pi,jp_{i,j}
小 F 希望你幫他想出一個保證所有礦井電力供應的最小花費方案。
輸入格式
第一行包含一個整數 n,表示礦井總數。
接下來 n 行,每行一個整數,第 i 個數 vi 表示在第 i 口礦井上建立發電站的費用。
接下來爲一個 n×n 的矩陣 P,其中 pi,jp_{i,j} 表示在第 i口礦井和第 j口礦井之間建立電網的費用。
數據保證 pi,j=pj,ip_{i,j}=p_{j,i},且 pi,ip_{i,i}=0。
輸出格式
輸出一個整數,表示讓所有礦井獲得充足電能的最小花費。

數據範圍
1n3001≤n≤300
0vi,pi,j1050≤vi,p_{i,j}≤10^5
輸入樣例:
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
輸出樣例:
9

思路:

如果不考慮方法(1),則這就是個最小生成樹模板題,但由於礦井必須連上發電廠才能供電,所以我們將發電場視爲點0(設礦井從1到n編號),在i上建立發電站認爲是從0到i建立一條邊
然後簡單證明下:首先只有一個發電站就是普通最小生成樹,肯定正確;當存在多個發電站時,因爲建了發電站的礦井已經通過0點連通了,其他點的連通問題就轉換成了0~n這(n+1)個點的最小生成樹。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int x[310][310],n,dis[310];
bool vis[310];
int prime(){
    int ans=0;
    memset(dis,0x3f,sizeof(dis));
    dis[0]=0;
    for(int i=0;i<=n;i++){
        int s=-1;
        for(int j=0;j<=n;j++){
            if(!vis[j]&&(s==-1||dis[j]<dis[s]))
                s=j;
        }
        vis[s]=true;
        ans+=dis[s];
        for(int j=0;j<=n;j++){
            dis[j]=min(dis[j],x[s][j]);
        }
    }
    return ans;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&x[0][i]);
        x[i][0]=x[0][i];
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&x[i][j]);
        }
    }
    printf("%d\n",prime());
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章