洛谷 題解 P1550 【[USACO08OCT]打井Watering Hole】

本題看似很難,實際上思路很是簡單——若是你想通了。c++

首先有一個問題:圖中有幾個點?大部分的人會回答\(n\)個點。錯了,有\(n+1\)個。spa

多出來的那個點在哪?關鍵在於你要理解每個決策的意義。實際上,多出來的那個點是地下的自然礦泉水。當咱們打井時,咱們其實是在往地下連邊。理解了這一點,代碼就沒有任何難度了。code

構圖時,咱們只需多加一個點,對於每一個點\(i\),咱們連邊\(i→n+1\),邊權爲\(w_i\)。而後直接跑最小生成樹就沒了。就沒了。(轉載from here)blog

#include<bits/stdc++.h>
using namespace std;
const int MAXN=300+10;
const int MAXM=1e5;
int n,m;
int fa[MAXN];
struct Node
{
    int u,v,w;
    bool operator < (const Node &x) const
    {
        return x.w>w;
    }
}edge[MAXM];
inline int read()
{
    int tot=0;
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    while(c>='0'&&c<='9')
    {
        tot=tot*10+c-'0';
        c=getchar();
    }
    return tot;
}
inline int find(int k)
{
    if(fa[k]==k)return k;
    else return fa[k]=find(fa[k]);
}
inline int kruskal()
{
    int tot=0,cnt=0;
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        int fx=find(edge[i].u),fy=find(edge[i].v);
        if(fx!=fy)
        {
            fa[fx]=fy;
            tot++;
            cnt+=edge[i].w;
        }
        if(tot==n-1)return cnt;
    }
}
int main()
{
    n=read();
    int x;
    for(int i=1;i<=n;i++)
    {
        x=read();
        edge[++m].u=i;
        edge[m].v=n+1;
        edge[m].w=x;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            x=read();
            if(i<j)
            {
                edge[++m].u=i;
                edge[m].v=j;
                edge[m].w=x;
            }
        }
    }
    n++;
    sort(edge+1,edge+1+m);
    printf("%d\n",kruskal());
    return 0;
}
相關文章
相關標籤/搜索