時限: 1000MS | 內存: 131072KB | 64位IO格式: %lld & %llu |
問題描述點擊打開鏈接
市政府“惠民工程”的目標是在全市n個居民點間之架設煤氣管道(但不一定有直接的管道相連,只要能間接通過管道可達即可)。很顯然最多可架設 n(n-1)/2條管道,然而實際上要連通n個居民點只需架設n-1條管道就可以了。現請你編寫程序,計算出該惠民工程需要的最低成本。
輸入
測試輸入包含若干測試用例。每個測試用例的第1行給出居民點數目M ( < =100 )、 評估的管道條數 N;隨後的 N 行對應居民點間管道的成本,每行給出一對正整數,分別是兩個居民點的編號,以及此兩居民點間管道的成本(也是正整數)。爲簡單起見,居民點從1到M編號。
輸出
對每個測試用例,在1行裏輸出全市管道暢通所需要的最低成本。若統計數據不足以保證暢通,則輸出“?”。
樣例輸入
3 3 1 2 1 1 3 2 2 3 4 3 1 2 3 2
樣例輸出
3 ?
思路:
本題與hdoj 1863 暢通工程點擊打開鏈接類似,典型的並查集,或者說是最小生成樹得題目!
#include<cstdio>
#include<cstring>
#include<stdlib.h>
#include<algorithm>
using namespace std;
int pre[1005];
struct node
{
int p1;
int p2;//p1,p2分別對應的兩個居民點
int v;//鋪設管道所需的費用
} way[1005];
int m,n;
void init()
{
memset(pre,0,sizeof(pre));
memset(way,0,sizeof(way));
for(int i=1;i<=m;i++)//初始化,每一個點當做是一個父節點
pre[i]=i;
}
int cmp(node a,node b)
{
return a.v<b.v;
}
int find(int x)//查找父節點
{
int ret=x;
while(pre[ret]!=ret) //如果查找到的父節點不是它本身,就直接把它當做一個新的父節點
ret=pre[ret];
int t=x,r;//下面是路徑壓縮
while(t!=ret)
{
r=pre[t]; //在改變上一級額的父節點之前 用臨時變量將它記錄起來
pre[t]=ret;//把上級節點改爲父節點
t=r;
}
return ret;
}
int join(int x,int y) //判斷兩個點是否連通
{
int fx=find(x);
int fy=find(y);
if(fx!=fy)//如果已經聯通就不用管,否則將它併入之前的連通分支中
{
pre[fx]=fy;
return 1;
}
return 0;
}
int main()
{
while(~scanf("%d%d",&m,&n))
{
init();
for(int i=0;i<n;i++)
scanf("%d%d%d",&way[i].p1,&way[i].p2,&way[i].v);
sort(way,way+n,cmp); //排序,目的是爲了優先考慮成本低的將它併入連通分支
int ans=0;
for(int i=0;i<n;i++)
if(join(way[i].p1,way[i].p2))//如果已經聯通就只需計算管道費用就行
ans+=way[i].v;
int count=0;
for(int i=1;i<=m;i++)
if(pre[i]==i)//判斷是否都連通
count++;
if(count>1)//如果不連通
printf("?\n");
else
printf("%d\n",ans);
}
return 0;
}