城市通电(kruscal)

题目链接:3728. 城市通电 - AcWing题库

 样例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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值