打卡信奥刷题(3583)用C++实现信奥题 P11514 [CCO 2024] Treasure Hunt

P11514 [CCO 2024] Treasure Hunt

题目背景

翻译自 CCO2024 P1

题目描述

佩里海盗正在航行七大洋!他有一张由 N N N 个岛屿和 M M M 条海路组成的地图。第 i i i 条海路连接岛屿 a i a_i ai b i b_i bi,双向通行的费用为 c i c_i ci 金币。与海怪交战代价不菲。为了寻找下一个大笔战利品,佩里已经侦查过每个岛屿,确定第 i i i 个岛上有一个装有 v i v_i vi 金币的宝箱。

他打算航行一段(可能为空)的海路,从岛屿 x x x 出发到岛屿 y y y 结束。旅程结束时,他会打开岛屿 y y y 的宝箱并获得战利品。这里的问题是:佩里并不知道自己当前在哪个岛上!因此,对于每个可能的起始岛 x x x,他想知道从该岛出发所有可能旅程中能够获得的最大净收益(假设他有足够的金币支付沿途的通行费用;他只关心下一次旅程的净利润)。

输入格式

第一行两个整数 n , m n, m n,m

第二行 n n n 个整数 v i v_i vi

往后 m m m 行每行三个整数 a i , b i , c i a_i, b_i, c_i ai,bi,ci,其描述了一条海路 i i i

保证无重边无自环。

输出格式

n n n 行,每行一个整数,第 i i i 行的整数代表以点 i i i 为起点岛屿所获得的最大可能的硬币数。

输入输出样例 #1

输入 #1

4 5
6 5 9 2
1 2 0
3 2 3
4 3 6
1 3 5
2 4 2

输出 #1

6
6
9
4

说明/提示

数据范围

Subtask分数约束
Subtask 0 0 0 0 0 0样例
Subtask 1 1 1 20 20 20 1 ≤ n ≤ 3000 1 \le n \le 3000 1n3000 0 ≤ m ≤ 3000 0 \le m \le 3000 0m3000
Subtask 2 2 2 20 20 20 1 ≤ n ≤ 10 6 1 \le n \le 10^6 1n106 0 ≤ m ≤ 10 6 0 \le m \le 10^6 0m106,对于每个岛屿 i i i c i = 0 c_i=0 ci=0 c i = 10 9 c_i=10^9 ci=109
Subtask 3 3 3 28 28 28 1 ≤ n ≤ 10 6 1 \le n \le 10^6 1n106 0 ≤ m ≤ 10 6 0 \le m \le 10^6 0m106,任意一对岛屿之间恰好有一条路径
Subtask 4 4 4 32 32 32无特殊限制

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 10 6 1 \le n \le 10^6 1n106 0 ≤ m ≤ 10 6 0 \le m \le 10^6 0m106 0 ≤ v i , c i ≤ 10 9 0 \le v_i,c_i \le 10^9 0vi,ci109 1 ≤ a i , b i ≤ n 1 \le a_i,b_i \le n 1ai,bin

样例解释 #1

对于第一个和第三个岛屿,最好待在岛上并打开岛上的箱子。

对于第二个岛屿,佩里可以前往第一个岛屿并打开那里的箱子。这将带来 6 − 0 = 6 6 - 0 = 6 60=6 枚硬币的净收益,是最佳的净收益。

对于第四个岛屿,佩里可以前往第二个和第三个岛屿并打开那里的箱子。这将带来 9 − 2 − 3 = 4 9 - 2 - 3 = 4 923=4 枚硬币的净收益,是最佳的净收益。

C++实现

#include <bits/stdc++.h>
using namespace std; 
#define pi pair<int, int>
const int N = 3000001; //这里是无向图 + 超级源点 所以要开 n+m*2 
int cnt, h[N], nex[N], to[N], w[N]; //链式前向星存图 
int n, m, dis[N];
bool vi[N]; 
void ad(int u, int v, int ww) {
	nex[++cnt] = h[u];
	to[cnt] = v;
	w[cnt] = ww;
	h[u] = cnt;
}
void dj(int s) { //模板 这里不多说 
	memset(dis, 0x3f, sizeof dis);
	memset(vi, 0, sizeof vi); //memset常数大但我爱用 
	priority_queue<pi, vector<pi>, greater<pi> > q;
	q.push({0, s});
	while(!q.empty()) { 
		int u=q.top().second, d=q.top().first;
		q.pop();
		if(vi[u]) continue;
		vi[u] = 1;
		for(int i=h[u]; i; i=nex[i]) {
			int v=to[i], ww=w[i];
			if(d+ww < dis[v]) {
				dis[v] = d+ww;
				q.push({dis[v], v});
			}
		}
	}
}
int main() {
	scanf("%d%d", &n, &m);
	for(int i=1, ww; i<=n; i++)
		scanf("%d", &ww), ad(0, i, -ww); //超级源点 
	while(m--) {
		int a, b, c; scanf("%d%d%d", &a, &b, &c);
		ad(a, b, c); ad(b, a, c); //由于是无向图所以要建双向边 
	}
	dj(0); //从超级源点开始跑 
	for(int i=1; i<=n; i++)
		printf("%d\n", -dis[i]); 
	return 0;
}

在这里插入图片描述

后续

接下来我会不断用C++来实现信奥比赛中的算法题、GESP考级编程题实现、白名单赛事考题实现,记录日常的编程生活、比赛心得,感兴趣的请关注,我后续将继续分享相关内容

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值