D. Shichikuji and Power Grid -Codeforces Round #597 (Div. 2)

D. Shichikuji and Power Grid -Codeforces Round #597 (Div. 2)_第1张图片

 

D. Shichikuji and Power Grid -Codeforces Round #597 (Div. 2)_第2张图片

D. Shichikuji and Power Grid -Codeforces Round #597 (Div. 2)_第3张图片

 

D. Shichikuji and Power Grid -Codeforces Round #597 (Div. 2)_第4张图片

这个题目的主要意思是:

输入一个数字n,然后输入n行坐标,第一个坐标是x,第二个坐标是y,然后再输入n个数字,第一个数字是在(x1,y1)处建立发电站花费的钱,第二个数字是在(x2,y2)处建立发电站需要的钱..

下面这一行是在(x1,y1)处需要连接电线要花的钱,比如你想从一个有发电站的(x2,y2)处拉扯电线到(x1,y1)处,那么你需要花费(k1+k2)*(abs(x2-x1)+(y1-y2))费用的钱;

而这个题最终的目的是让每个地方都有电,并且有电的城市我们就认为是联通的,在这样的前提下,我们花费最少,这样我们就很容易想到了最小生成树的模板,一个是prim算法,另一个是Kruskal算法;下面是我转载的对于这两个算法的介绍说明:

转载:https://blog.csdn.net/qq_35644234/article/details/59106779#commentBox

我之前只是学过这算法,但是并没有做过相关的题,导致只知道理论,但是无法把理论付诸于实践,对于这个题,推荐使用prim算法,但是我感觉Kruskal算法好理解一些,但是这个算法的内存占用比较大:

下面是kruskal算法的思路:

  先得建图,把1 - n的每个点都得建立,这里用邻接表来存,(这里我们直接把0-i的点的距离初始化为c[i],也就是权值)然后把i -> j的点初始化为(abs(x[i]-x[j])+abs(y[i]-y[j])*(k[i]+k[j]));构建完图之后我们就可以根据权值进行排序了,当然我们再使用kruskal算法的时候也得使用并查集(这个网上有很多模板,而且简单粗暴),下面就是按照题目要求写代码了,下面这个代码是我在比赛结束后看别人写的代码,然后修改了一下我认为不好的地方:

#include 
#include 
#include 
#include 
typedef long long ll;
using namespace std;
const int maxn = 2005;
int par[maxn];
// 并查集 
int find(int x){
	if(par[x]!=x)par[x] = find(par[x]) ;
	return par[x];
}
// 邻接表 
struct edge {
	int u,v;
	ll w;
	// 重载运算符 
	bool operator < (const edge & e) const {
		return w < e.w;
	}
}es[maxn*maxn+maxn];

int tot = 0;
int x[maxn],y[maxn],c[maxn],k[maxn];
int main()
{
	// 输入 
	int n;
	cin>>n;
	for(int i = 1;i <= n;i++) par[i]=i;
	for(int i = 1;i <= n;i++) cin>>x[i]>>y[i];
	for(int i = 1;i <= n;i++) cin>>c[i];
	for(int i = 1;i <= n;i++) cin>>k[i];
	
	// 建立 原点到 i的距离,也就是建立基站的大小 
	for(int i = 1;i <= n;i++) es[++tot] = {0,i,c[i]};
	
	for(int i=1; i<=n;i++)
	{
		for(int j=i+1; j<=n;j++)
		{
			// 建立 i -> j 的边权关系 
			es[++tot] = {i,j,1ll*(k[i]+k[j])*(abs(x[i]-x[j])+abs(y[i]-y[j]))};
		}
			
	}
	
	// 按照边权进行排序 
	sort(es+1,es+1+tot);
	ll ans = 0;
	vector>v;
	vectorp;
	for(int i=1; i<=tot; ++i)
	{
		int x = find(es[i].u),y = find(es[i].v);
		if(x != y)
		{
			par[x] = y;
			
			if(es[i].u == 0)
			{
				// 判断这个是不是基站 
				p.push_back(es[i].v);
			}
			else
			{
				//这个站不是基站,所以得通过连线来解决这个问题 
				v.push_back({es[i].u, es[i].v});
			}
			ans += es[i].w;
		}	
	}
	
	cout<

至于prim算法的题解也是有的,但是有个地方我不是很懂,等我懂了,我再把这个地方补充一下:

#include  
#include 
#include 
#include 
using namespace std;

typedef long long LL;
int n;
const int N = 2010;
LL dist[N];
LL dist1[N];
int x[N],y[N];
int val[N];
bool st[N];

typedef pair PII;

vectoredge;
vectorpw;
PII var[N];
int main()
{
	cin >> n;
	for(int i = 1; i <= n;i++)cin >> x[i]>>y[i];
	
	for(int i = 1; i <= n;i++)cin >> dist[i];
	
	for(int i = 1; i <= n;i++)cin >> val[i];
	
	LL res = 0;
	
	for(int i = 0; i < n; i++)
	{
		int t = -1;
		
		for(int j = 1;j <= n;j++)
		{
			if(!st[j]&&(t==-1||dist[t] > dist[j])){
				t = j;
			}
		}
		
		res += dist[t];
		st[t] = true;
		
		for(int j = 1; j <= n;j++)
		{
			if(j==t)continue;
			int pd = dist[j];
			dist[j] = min(dist[j],1ll*(val[t]+val[j])*(abs(x[t] - x[j]) + abs(y[t] - y[j])));
			if(dist[j] != pd)
			{
				// 这里不是很懂 
				var[j] = {t,j};
			}
		}
		
		//cout <<"t = " << t << endl;
		// 这里也不是很懂 
		if(var[t].first)
		{
			edge.push_back(var[t]);
		}
		else
		{
			pw.push_back(t);
		}
	}
	
	cout << res << endl;
	cout << pw.size() << endl;
	for(auto &x : pw)cout <

 

你可能感兴趣的:(#,最小生成树)