https://www.luogu.com.cn/problem/P1550
题目描述
Farmer John 的农场缺水了。
他决定将水引入到他的 nn(1 \leq n \leq 3001≤n≤300)个牧场。他准备通过挖若干井,并在各块田中修筑水道来连通各块田地以供水。在第 ii 号田中挖一口井需要花费 W_iWi(1 \leq W_i \leq 10^51≤Wi≤105)元。连接 ii 号田与 jj 号田需要 P_{i,j}Pi,j(1 \leq P_{i,j} \leq 10^51≤Pi,j≤105,且 P_{j,i}=P_{i,j}Pj,i=Pi,j)元。
请求出 FJ 需要为使所有农场都与有水的农场相连或拥有水井所需要的最少钱数。
输入格式
第一行为一个整数 nn。
接下来 nn 每行一个整数 W_iWi。
接下来 nn 行,每行 nn 个整数,第 ii 行的第 jj 个数表示连接 ii 号田和 jj 号田需要的费用 P_{i,j}Pi,j。
输出格式
输出最小开销。
输入输出样例
输入 #1复制
4 5 4 4 3 0 2 2 2 2 0 3 3 2 3 0 4 2 3 4 0
输出 #1复制
9
思路:边权和点权结合的套路题,将点权转化为到一个虚拟源点的边权,然后再贪心做最小生成树
先想象有一张平面图上面全部点通过最小生成树连接,形成一张边权最小的连通。
然后在这个图的上端或者其他端,放一个虚拟原点,总之想象成类似锥体的样子,锥顶点到平面图上各个点的边的边权就是建造水井的代价。
再跑最小生成树跑出来就是正确答案。
下面进行简单的解释:
加上虚拟源点到平面上各个点的最小生成树结合平面上的最小生成树的连边能形成整个立体图的最小生成树。
那么这个时候表示的意义就是连通整张图的最小代价,而由于要保证连通,所以虚拟源点至少有一个边和平面图上的点相连。不然就是有至少两个连通分量。
那么我们赋予这个为造水井的代价的时候,就满足题意造水井并且图连通的最小代价。
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=50000;
typedef long long LL;
struct rec{
LL start,end,p;
}edge[maxn];
bool operator <(rec a,rec b)
{
return a.p<b.p;
}
struct node{
LL w;
}nod[310];
LL fa[maxn],n,m,ans=0,cnt=0;
LL get(LL x)
{
if(x==fa[x]) return x;
return fa[x]=get(fa[x]);
}
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
cin>>n;
for(LL i=1;i<=n;i++)
{
LL a;cin>>a;nod[i].w=a;
}
for(LL i=1;i<=n;i++)
for(LL j=1;j<=n;j++)
{
LL a;cin>>a;
if(i<=j) continue;
edge[++cnt].start=i,edge[cnt].end=j,edge[cnt].p=a;
}
for(LL i=1;i<=n;i++)
{
edge[++cnt].start=i,edge[cnt].end=n+1,edge[cnt].p=nod[i].w;
}
sort(edge+1,edge+1+cnt);
for(LL i=1;i<=n+1;i++) fa[i]=i;
for(LL i=1;i<=cnt;i++)
{
LL x=get(edge[i].start);
LL y=get(edge[i].end);
if(x==y) continue;
fa[x]=y;
ans+=edge[i].p;
}
cout<<ans<<endl;
return 0;
}