NOIP 2009 最优贸易

本文介绍了一种解决有向图中特定路径最值问题的算法,目标是从起点到终点找到一条路径,使得路径上两点按拓扑序先后经过时,后点权值减前点权值的总和最大。通过两次SPFA算法分别预处理从起点到各点及从各点到终点的最值,最终遍历所有点找到最大差值。
摘要由CSDN通过智能技术生成

题目链接  https://ac.nowcoder.com/acm/contest/257/C

首先明确,这是一个有向图,但是权值并不在边上,权值在图中的一些顶点上。本题相当于求解的是,从1到n的一条路径,使得路径上的两个按照拓扑序先后经过的点后经过的点的权值减去先经过的点权值的值最大

挺绕的,,,慢慢理解。。。

如果直到了从 1 到点 i 最小点权,又知道了从 i 到 n 的最大点权,就可以遍历所有的点,然后从所有的差值中选取最大的即为正确答案。

因此本题转换为求从 1 到所有点的最大点权和 从任意点到 n 的最大点权,对于第一个,可以直接在原图上跑最短路算法,并对最短路做稍稍修改。原本最短路中的松弛操作是 

d[x] = min(d[x], d[y] + dist);

但是现在求解的已经不是经过路径上所有权值的和了,而是最小的,因此松弛操作改为

d[x] = min(d[x], min(d[y], price[x]));

对于第二个,可以建反图,在反图上以 n 为源点跑最短路,最后遍历这样两个数组求最大差值即可。

/**
 * Author : correct
 */
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define mem(a, b) memset(a, b, sizeof a)
const int N = 100100, M = 500100;
const char* path = "D:\\eclipse for c++ workspace\\test\\src\\in.in";
int head[N], nex[M * 5], to[M * 5], cnt;
int rhead[N];
void add(int* h, int a, int b){
	++cnt;
	to[cnt] = b;
	nex[cnt] = h[a];
	h[a] = cnt;
}
int price[N];
int n, m;
int df[N], dt[N];
bool vis[N];
queue<int > q;
void spfa(){
	mem(df, 0x3f);
	mem(vis, 0);
	df[1] = price[1];
	q.push(1);
	vis[1] = 1;
	while (!q.empty()){
		int t = q.front();
		q.pop();
		vis[t] = 0;
		for (int i = head[t]; i; i = nex[i]){
			int y = to[i];
			if (df[y] > min(df[t], price[y])){
				df[y] = min(df[t], price[y]);
				if (vis[y] == 0){
					vis[y] = 1;
					q.push(y);
				}
			}
		}
	}
}
void rspfa(){
	mem(dt, 0);
	mem(vis, 0);
	dt[n] = price[n];
	q.push(n);
	vis[n] = 1;
	while (q.size()){
		int t = q.front();
		q.pop();
		vis[t] = 0;
		for (int i = rhead[t]; i; i = nex[i]){
			int y = to[i];
			if (dt[y] < max(dt[t], price[y])){
				dt[y] = max(dt[t], price[y]);
				if (vis[y] == 0){
					vis[y] = 1;
					q.push(y);
				}

			}
		}
	}
}
int main()
{
	freopen(path, "r", stdin);
	ios::sync_with_stdio(0);
	cnt = 0;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> price[i];
	for (int i = 1; i <= m; i++){
		int a, b, c;
		cin >> a >> b >> c;
		add(head, a, b);
		add(rhead, b, a);
		if (c == 2)add(head, b, a), add(rhead, b, a);
	}
	spfa();
	rspfa();
	int ans = -1;
	for (int i = 1; i <= n; i++){
//		cout << df[i] << " -> " << dt[i] << "\n";
		ans = max(ans, dt[i] - df[i]);
	}
	cout << ans << "\n";
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值