样例1输入:
3
2 3
1 1
3 2
3 2 3
3 2 3
样例1输出:
8
3
1 2 3
0
样例2输入:
3
2 1
1 2
3 3
23 2 23
3 2 3
样例2输出:
27
1
2
2
1 2
2 3
分析:这道题我用两种解题思路来进行分析:
思路一:
我们首先考虑把所有点都用建发电站的方式来使其通电,那么接下来我们按照kruscal求解最小生成树的思想遍历按照权值从小到大遍历所有边,那么我们考虑一条边如果是在一个连通块内那么就不作考虑,如果其不在一个连通块内,那么我们先考虑两个连通块建立发电站的代价,如果两个连通块分别建立发电站的代价都小于等于连边的代价,那么我们就没必要连边,继续考虑下一条边,如果要是存在一个连通块建立发电站的代价大于连边的代价,那么我们就可以把建立发电站代价较大的那个点去掉,选择通过连边来使其通电,我们可以用一个连通块中建立发电站代价最小的点来作为根节点,这样会比较容易维护,至于输出的变量直接在过程中维护一下即可。
思路二:
我们建立一个虚拟原点0,然后在每个点建立发电站等价于从0点向该点连一条边,边权就是在该点建立发电站的代价,然后我们直接对这n+1个点跑一个kruscal即可,同理也是过程中维护输出量
思路一代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=2003;
int x[N],y[N];
long long c[N],k[N];
int fu[N];
bool vis[N];
struct node{
int u,v;
long long w;
}p[N*N];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(fu[x]!=x) return fu[x]=find(fu[x]);
return x;
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x[i],&y[i]);
fu[i]=i;
}
long long ans=0;
for(int i=1;i<=n;i++)
scanf("%lld",&c[i]),ans+=c[i];
for(int i=1;i<=n;i++)
scanf("%lld",&k[i]);
int tt=0;
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
p[++tt]={i,j,(k[i]+k[j])*(abs(x[i]-x[j])+abs(y[i]-y[j]))};
sort(p+1,p+tt+1,cmp);
vector<int> v;//存储哪些位置不需要建立发电站
vector<pair<int,int> >vv;
for(int i=1;i<=tt;i++)
{
int f1=find(p[i].u),f2=find(p[i].v);
if(c[f1]>c[f2]) swap(f1,f2);
if(f1==f2) continue;
if(c[f2]>p[i].w)
{
v.push_back(f2);
vv.push_back(make_pair(p[i].u,p[i].v));
fu[f2]=f1;
ans-=c[f2]-p[i].w;
}
}
printf("%lld\n%d\n",ans,n-(int)v.size());
for(int i=0;i<v.size();i++)
vis[v[i]]=true;
for(int i=1;i<=n;i++)
if(!vis[i]) printf("%d ",i);
printf("\n%d\n",(int)vv.size());
for(int i=0;i<vv.size();i++)
printf("%d %d\n",vv[i].first,vv[i].second);
return 0;
}
思路二代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=2003;
int x[N],y[N];
long long c[N],k[N];
int fu[N];
bool vis[N];
struct node{
int u,v;
long long w;
}p[N*N];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(fu[x]!=x) return fu[x]=find(fu[x]);
return x;
}
int main()
{
int n;
cin>>n;
int tt=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x[i],&y[i]);
fu[i]=i;
}
for(int i=1;i<=n;i++)
{
scanf("%lld",&c[i]);
p[++tt]={0,i,c[i]};
}
for(int i=1;i<=n;i++)
scanf("%lld",&k[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
p[++tt]={j,i,(k[i]+k[j])*(abs(x[i]-x[j])+abs(y[i]-y[j]))};
sort(p+1,p+tt+1,cmp);
vector<int> v;//存储哪些位置需要建立发电站
vector<pair<int,int> >vv;//存储哪些点对之间需要连边
long long ans=0;
int cnt=n;
for(int i=1;i<=tt;i++)
{
int f1=find(p[i].u),f2=find(p[i].v);
if(f1==f2) continue;
fu[f2]=f1;
if(!p[i].u)//该点是超级原点,说明在p[i].v处建立发电站
v.push_back(p[i].v);
else
vv.push_back(make_pair(p[i].u,p[i].v));
ans+=p[i].w;
cnt--;
if(!cnt) break;
}
printf("%lld\n%d\n",ans,(int)v.size());
for(int i=0;i<v.size();i++)
printf("%d ",v[i]);
printf("\n%d\n",(int)vv.size());
for(int i=0;i<vv.size();i++)
printf("%d %d\n",vv[i].first,vv[i].second);
return 0;
}