解题思路:这个题乍一看就是一个最小生成树的题目,这个一般都能看出来,但这个题的难点在于细节比较多,真的到处是坑QAQ(看了解析后自己写还是交了10几发才过了),对于这种题我们要慢慢一点一点地来分析题意。
细节1:题目在输入的时候告诉我们,在两个城市之间修建道路的时候,道路的花费可以是负数,表示修完路后能够赚钱,对于这种情况,我们在求最小生成树的时候,不管当前的两个城市是否是连通的都要修这条路,毕竟谁又能拒绝赚钱的好事呢。
细节2:题目输入的时候又说至少保证任意两个城市可以通过道路和码头实现连通,所以说只用道路或者是只用码头很有可能不能将所有城市连通,如果只用道路或码头就能把所有城市都连通起来,那么再加上另一种交通方式一定也能使所有城市连通,这种情况下我们就需要将这些方案的结果都算出来求一个最小值,如果说只用道路或码头不能使所有城市连通,那么答案就只能是道路和码头混合使用的情况了。
细节3:因为我们很有可能会用到码头,所以要考虑如何将修建码头的情况给加入到最小生成树中,我们这里是把河道设为0号点,如果可以在该城市处修建码头的话,就在河道和该城市之间建一条权值为w[i](修建码头的代价)的边。
细节4(是对2和3的补充):有细节3可知我们在原有的城市中又多出来了一个“城市”——河道,所以在对并查集的father数组进行初始化的时候,要从0开始初始化,不能再习惯性从1开始初始化了,针对细节2,我们在判断只用一种交通方式就可以把所有城市都连通的情况下,我们首先要把只用一种交通方式的最小生成树给求出来,然后再求两种方式混合的最小生成树,因为我们是把所有的道路一次性都加入到了求最小生成树的结构体中,那怎样对道路和码头进行区分呢?我们在对结构体进行赋值的时候,是先将修建道路的信息放入到结构体中,然后才放入码头的信息,所以结构体的前m个数据是道路的,后面的是码头的,所以在求只用道路构建的最小生成树的时候,我们就只需要对前m个数据进行排序就行了。求混合情况的时候再对所有数据排序。
上代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
const int N=2E5+10;
long long w[N],p[N];
int n,m,idx;
struct node{
int u,v;
int s;
}edge[N];
int find(int x)
{
if(x!=p[x])
p[x]=find(p[x]);
return p[x];
}
bool cmp(struct node a,struct node b)
{
return a.s<b.s;
}
long long krusal(int m)
{
long long ans=0;
for(int i=1;i<=m;i++)
{
int u=edge[i].u,v=edge[i].v;
int fu=find(u),fv=find(v);
if(fu!=fv||edge[i].s<0)// 如果当前道路能赚钱肯定要建
{
p[fu]=fv;
ans+=edge[i].s;
}
}
return ans;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int u,v,c;
cin>>u>>v>>c;
edge[i]={u,v,c};
}
idx=m+1;
for(int i=1;i<=n;i++)
{
cin>>w[i];
if(w[i]!=-1)
edge[idx++]={0,i,w[i]};
}
for(int i=0;i<=n;i++)
p[i]=i;
sort(edge+1,edge+m+1,cmp);
long long ans=0;
for(int i=1;i<=m;i++)
{
int u=edge[i].u,v=edge[i].v;
int fu=find(u),fv=find(v);
p[fu]=fv;
}
int x=1;
for(x=2;x<=n;x++)
if(find(1)!=find(x))//检查只用道路能不能将所有城市连起来
break;
if(x==n+1)//说明只用道路是可以把所有城市都连起来的
{
long long ans=10000000000000;
for(int i=0;i<=n;i++)
p[i]=i;
ans=min(ans,krusal(m));
for(int i=0;i<=n;i++)
p[i]=i;
sort(edge+1,edge+idx,cmp);
ans=min(ans,krusal(idx-1));
cout<<ans<<endl;
}
else
{
for(int i=0;i<=n;i++)
p[i]=i;
sort(edge+1,edge+idx,cmp);
cout<<krusal(idx-1)<<endl;
}
//我们在解题思路的说明中说了3中情况,可通过上面的代码发现我们好像只考虑了其中2中情况,为什么就是对的呢?
//上面的代码明面上看起来是只考虑了只用道路和用道路,码头混合这两种情况,没有考虑只用码头的情况
//但是仔细想一下,如果说最优的情况是只用码头就可以构建的话,那么这种情况在求混用的情况是不是就会算出来呢?
return 0;
}