CodeForces - 1245D Shichikuji and Power Grid (prim、不建立虚拟结点版)

题目链接:https://codeforces.com/problemset/problem/1245/D

大致思路:

  1. prim比kruskal快很多,所以用了prim。prim一个样例基本50ms就跑出来了,kruskal要300+ms
  2. 贪心,肯定是需要一个城市自建发电站的,那么选取一个花费最少的城市修建发电站
  3. city结构体记录是键发电站,还是连接城市
  4. 每次照prim模板更新dist就可以了
  5. 连线的前驱结点,遍历城市,如果花费和找到点的花费相同,那么就能作为前驱结点
  6. 开long long,我一开始开的int,花费那儿爆掉了

代码:

//#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e3 + 5;
const int INF = 0x3f3f3f3f;

struct City {
	PII pos;
	ll id;//城市的编号
	ll ci;//题意中的ci
	ll ki;//题意中的ki
	ll flg;//0表示选择ci   1表示选择ki
	bool st = false;//是否加入到了连通集合
	bool operator<(const City &C)const {
		return ci < C.ci;//用来找选取的第一个自建发电站的城市
	}
} city[N];

int n;
ll dist[N];
ll cnt;//添加城市的编号
set<PII> reck;//flg == 1   连线
set<ll> recc;//flg == 0    自建发电站

inline ll kcost(City x, City y) {//计算两个城市连电缆的花费
	return (x.ki + y.ki) * (abs(x.pos.first - y.pos.first) + abs(x.pos.second - y.pos.second));
}

ll prim() {
	memset(dist, 0x3f, sizeof dist);
	dist[0] = city[0].ci;
	recc.insert(city[0].id);
	city[0].st = true;
	for (int i = 1; i < n; i++) {
		if (!city[i].st) {
			if (dist[i] >= city[i].ci) {
				dist[i] = city[i].ci;
				city[i].flg = 0;
			}
			if (dist[i] > kcost(city[0], city[i])) {
				dist[i] = kcost(city[0], city[i]);
				city[i].flg = 1;
			}
		}
	}
	ll ans = city[0].ci;
	for (int i = 1; i < n; i++) {
		int t = -1;
		for (int j = 1; j < n; j++)
			if (!city[j].st && (t == -1 || dist[t] > dist[j])) {
				t = j;
			}
		if (city[t].flg) {
			for (int j = 0; j < n; j++) {
				if (kcost(city[j], city[t]) == dist[t])
					reck.insert({min(city[t].id, city[j].id), max(city[t].id, city[j].id)});
					//这里min和max很重要,保持有序,防止出现答案中(a,b)  (b,a)这样的连两次的情况
			}
		} else
			recc.insert(city[t].id);
		ans += dist[t];
		
		//更新dist模板
		for (int j = 1; j < n; j++) {
			if (!city[j].st) {
				if (dist[j] >= city[j].ci) {
					dist[j] = city[j].ci;
					city[j].flg = 0;
				}
				if (dist[j] > kcost(city[t], city[j])) {
					dist[j] = kcost(city[t], city[j]);
					city[j].flg = 1;
				}
			}
		}
		city[t].st = true;
	}
	return ans;
}

int main(void) {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		int x, y;
		scanf("%d%d", &x, &y);
		city[i].pos = {x, y};
		city[i].id = ++cnt;
	}
	for (int i = 0; i < n; i++)
		scanf("%lld", &city[i].ci);
	for (int i = 0; i < n; i++)
		scanf("%lld", &city[i].ki);
	sort(city, city + n);
	cout << prim() << endl;
	cout << recc.size() << endl;
	for (auto i : recc)
		cout << i << " ";
	cout << endl << reck.size() << endl;
	for (auto i : reck)
		cout << i.first << " " << i.second << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值