Codeforces Round #597 (Div. 2) D. Shichikuji and Power Grid 最小生成树

题目:https://codeforces.com/contest/1245/problem/D

题意:

有n座城市(用二维坐标表示,坐标大小小于1e6)

城市要供电,因此需要在城市里面修发电站,不同城市里面修发电站的代价不一样,分别是:

c[1],c[2],……,c[n]

城市和城市之间也可以修电线,给你个数组: k[1],k[2],……,k[n], 然后i,j城市之间修电线的代价为 城市间曼哈顿距离*(k[i]+k[j])

城市有电当且仅当城市里面有发电站或者能通过电线直接或者间接的和有发电站的城市相连

问最小的代价是多少,需要在哪些城市里面修发电站,哪些城市里面修电线

 

 

 

最小生成树的题目。

首先两两城市之间建边,权值为他们修路的代价

然后要考虑城市里面修发电站的情况,因此使用超级源点s,s向每座城市建边,权值为c[i]

关于记录发电站或者修电线的话,程序里面有注释

#include<bits/stdc++.h>
#define ll long long 
#define ull unsigned long long
using namespace std;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> site;
const int maxn = 2005;
site num[maxn];
int c[maxn], k[maxn];
int Head[maxn*maxn], Nxt[maxn*maxn * 2], To[maxn*maxn * 2];
ll Val[maxn*maxn * 2];
int tot;
void add_edge(int fro, int to, ll val) {
	Nxt[++tot] = Head[fro];
	To[tot] = to;
	Val[tot] = val;
	Head[fro] = tot;
}
typedef pair<ll, site> P;
//这里的site的意思是 : site.first 为当前的点  site.second 为从那个点来的
priority_queue<P, vector<P>, greater<P> >Q;
ll dis[maxn];
int vis[maxn];
vector<int> ans_poi; //发电站
vector<site> ans_edge; //城市间修电线
ll Prim(int s,int ed) {
	ll ans = 0;
	int flag = 0;
	memset(dis, INF, sizeof(dis));
	dis[s] = 1;
	Q.push(P(0, site(s, -1)));
	P pp;
	while (!Q.empty()) {
		pp = Q.top();
		int now = Q.top().second.first;
		ll d = Q.top().first;
		Q.pop();
		if (vis[now])continue;
		vis[now] = 1;
		ans += d;
		if (pp.second.second != -1) {
			if (pp.second.first == 0) //有任何一个点是0的话说明是修发电站的
				ans_poi.push_back(pp.second.second);
			else if (pp.second.second == 0)
				ans_poi.push_back(pp.second.first);
			else //不然就是修电线的
				ans_edge.push_back(site(pp.second.first, pp.second.second));
		}
		if (++flag == ed) return ans;
		for (int i = Head[now]; i; i = Nxt[i]) {
			int to = To[i];
			ll val = Val[i];
			if (dis[to] > val) {
				dis[to] = val;
				Q.push(P(dis[to], site(to, now)));
			}
		}
	}
	return ans;
}

int main() {
	int n; cin >> n;
	for (int i = 1; i <= n; i++)
		scanf("%d %d", &num[i].first, &num[i].second);
	for (int i = 1; i <= n; i++)
		scanf("%d", c + i);
	for (int i = 1; i <= n; i++)
		scanf("%d", k + i);
	ll sto;
	for (int i = 1; i <= n; i++) { //城市之间建边
		for (int j = 1; j < i; j++) {
			sto = (ll)abs(num[i].first - num[j].first) + abs(num[i].second - num[j].second);
			sto *= (k[i] + k[j]);
			add_edge(i, j, sto);
			add_edge(j, i, sto);
		}
	}
	for (int i = 1; i <= n; i++) { //建发电站
		add_edge(0, i, (ll)c[i]);
		add_edge(i, 0, (ll)c[i]);
	}
	cout << Prim(0, n + 1) << endl; //最小生成树
	cout << ans_poi.size() << endl;
	for (int i = 0; i < ans_poi.size(); i++)
		cout << ans_poi[i] << " ";
	cout << endl;
	cout << ans_edge.size() << endl;
	for (int i = 0; i < ans_edge.size(); i++)
		cout << ans_edge[i].first << " " << ans_edge[i].second << endl;
	return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值