题目描述
有
N
N
N 个小岛,初始时每个小岛上没有飞机场,海湾与连接道路,现在可以
1.
花费
X
i
X_i
Xi 在小岛
i
i
i 上建造一个飞机场
2.
花费
Y
i
Y_i
Yi 在小岛
i
i
i 上建造一个海湾
3.
花费
Z
i
Z_i
Zi 连接 小岛
A
i
A_i
Ai 与
B
i
B_i
Bi
如果小岛
U
U
U ,
V
V
V 上都有飞机场,或者都有海湾,或者被连接,则为可达
现在需要所有岛都互相可达,求最小的花费
输入样例
4 2
1 20 4 7
20 2 20 3
1 3 5
1 4 6
输出样例
16
算法
(最小生成树) O ( n + m ) O(n + m) O(n+m)
1.
首先分析,需要图中所有点两两可达,那么就是最小生成树问题
2.
如何处理点权?
在这里可以设置两个虚拟源点
一个是
n
+
1
n+1
n+1 与其余
n
n
n 个点连接一条边,边权为
X
i
X_i
Xi
一个是
n
+
2
n+2
n+2 与其余
n
n
n 个点连接一条边,边权为
Y
i
Y_i
Yi
那么接下来我们就有四种情况
(1).
没有机场也没有海湾,那么最小生成树点的总数
N
=
n
N = n
N=n
(2).
只有机场,没有海湾,那么最小生成树点的总数
N
=
n
+
1
N = n + 1
N=n+1 并且需要加入边
X
i
X_i
Xi
(3).
只有海湾,没有机场,那么最小生成树点的总数
N
=
n
+
1
N = n + 1
N=n+1 并且需要加入边
Y
i
Y_i
Yi
(4).
既有机场,也有海湾,那么最小生成树点的总数
N
=
n
+
2
N = n + 2
N=n+2 并且需要加入边
X
i
X_i
Xi ,
Y
i
Y_i
Yi
在这里需要注意的是每次加入边了之后记得
i
n
i
t
init
init
最后还需要特判无解的情况,在这里我用的就是并查集维护size去判断是否无解
C++ 代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<unordered_map>
#include<unordered_set>
#include<cmath>
#include<map>
#include<set>
#include<stack>
#include<vector>
#include<deque>
#include<cmath>
#include<ctime>
using namespace std;
const int N=1e6+10;
typedef long long ll;
struct node
{
int a,b;
ll c;
bool operator<(const node &w) const
{
return c<w.c;
}
}e[N];
int f[N],w1[N],w2[N],A[N],B[N],siz[N];
ll C[N];
int n,m;
int find(int x)
{
if(x!=f[x])
f[x]=find(f[x]);
return f[x];
}
void init()
{
for(int i=0;i<N;i++)
{
e[i]={0,0,(ll)9e18};
}
for(int i=0;i<N;i++)
{
f[i]=i;
siz[i]=1;
}
for(int i=1;i<=m;i++)
{
e[i]={A[i],B[i],C[i]};
}
}
ll ku(int n,int m,int c)
{
init();
if(c==1)
{
for(int i=1;i<=n;i++)
e[++m]={n+1,i,1ll*w1[i]};
n++;
}
else if(c==2)
{
for(int i=1;i<=n;i++)
e[++m]={n+1,i,1ll*w2[i]};
n++;
}
else if(c==3)
{
for(int i=1;i<=n;i++)
e[++m]={n+1,i,1ll*w1[i]};
for(int i=1;i<=n;i++)
e[++m]={n+2,i,1ll*w2[i]};
n+=2;
}
sort(e+1,e+m+1);
ll res=0;
for(int i=1;i<=m;i++)
{
int a=e[i].a,b=e[i].b;
ll c=e[i].c;
a=find(a);
b=find(b);
if(a!=b)
{
f[a]=b;
siz[b]+=siz[a];
res+=c;
}
}
for(int i=1;i<=n;i++)
{
int x=find(i);
if(siz[x]!=n) return 9e18;
}
return res;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&w1[i]);
for(int i=1;i<=n;i++) scanf("%d",&w2[i]);
for(int i=1;i<=m;i++)
{
scanf("%d %d %lld",&A[i],&B[i],&C[i]);
}
ll res=9e18;
//不建立机场和海湾
res=min(res,ku(n,m,0));
//只建立机场
res=min(res,ku(n,m,1));
//只建立海湾
res=min(res,ku(n,m,2));
//建立机场+海湾
res=min(res,ku(n,m,3));
cout<<res<<endl;
}