• 【NOIP2009】最优贸易


    题目链接:https://www.luogu.com.cn/problem/P1073

    题目大意:给定一张有向图 , 其中每个点有一个权值 (w), 求出 (1)(n) 的所有路径中 (w_i - w_j) ( (i) , (j) 为同一条路径上的两个节点 , 且 (i)(j) 后遍历到 )​ 的最大值

    solution

    算法一:

    设从 1 到 (i) 所有路径上节点权值的最大值为 (g_i) , (i)(n) 所有路径上节点权值的最小值为 (f_i) , 可以用SPFA分别求出这两个值 , 再遍历每个节点 , 求得 (maxleft{g_i - f_i ight}(i leq n))即为答案

    复杂度: (O(km)) ~ (O(nm))

    code

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T> inline void read(T &FF) {
    	int RR = 1; FF = 0; char CH = getchar();
    	for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
    	for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
    	FF *= RR;
    }
    inline void file(string str) {
    	freopen((str + ".in").c_str(), "r", stdin);
    	freopen((str + ".out").c_str(), "w", stdout);
    }
    const int N = 1e6 + 10;
    int n, m, now, fst[N], nxt[N], num[N], wi[N], ans;
    int ni, ft[N], nt[N], nm[N];
    void add(int u, int v, int k) {
    	nxt[++now] = fst[u], fst[u] = now, num[now] = v;
    	nt[++ni] = ft[v], ft[v] = ni, nm[ni] = u;
    	if(k) {
    		nxt[++now] = fst[v], fst[v] = now, num[now] = u;
    		nt[++ni] = ft[u], ft[u] = ni, nm[ni] = v;
    	}
    }
    int vis[N], ma[N], mi[N];
    void Spfa_max() {
    	queue<int> qi;
    	qi.push(n); vis[n] = true; ma[n] = wi[n];
    	while(!qi.empty()) {
    		int pi = qi.front(); qi.pop(); vis[pi] = false;
    		for(int i = ft[pi]; i; i = nt[i]) 
    			if(max(ma[pi], wi[nm[i]]) > ma[nm[i]]) {
    				ma[nm[i]] = max(ma[pi], wi[nm[i]]);
    				if(!vis[nm[i]]) qi.push(nm[i]), vis[nm[i]] = true;
    			}
    	}
    }
    void Spfa_min() {
    	queue<int> qi;
    	memset(vis, 0, sizeof(vis));
    	memset(mi, 0x3f, sizeof(mi));
    	qi.push(1); vis[1] = true; mi[1] = wi[1];
    	while(!qi.empty()) {
    		int pi = qi.front(); qi.pop(); vis[pi] = false;
    		for(int i = fst[pi]; i; i = nxt[i])
    			if(min(mi[pi], wi[num[i]]) < mi[num[i]]) {
    				mi[num[i]] = min(mi[pi], wi[num[i]]);
    				if(!vis[num[i]]) qi.push(num[i]), vis[num[i]] = true;
    			}
    	}
    }
    int main() {
    	//file("");
    	int u, v, k;
    	read(n), read(m);
    	for(int i = 1; i <= n; i++) read(wi[i]);
    	for(int i = 1; i <= m; i++)
    		read(u), read(v), read(k), add(u, v, k - 1);
    	Spfa_max(), Spfa_min();
    	for(int i = 1; i <= n; i++)
    		ans = max(ans, ma[i] - mi[i]);
    	cout << ans << endl;
    	return 0;
    }
    

    算法二:

    建三张一模一样的分层图 , 其中每个分层图内路径长度为 0 , 相邻两张分层图中从 (i)(i) 连边 , 其中一号图与二号图间 (i) 号节点连边的长度设为 (-w_i) , 意为从 (i) 号节点买进 , 二号图与三号图间 (i) 号节点连边的长度设为 (w_i) 意为从 (i) 号节点卖出 , 这样从一号图中的 1号节点到三号图中 (n) 号节点的最长路即为答案 , 可用SPFA求出

    复杂度: (O(km)) ~ (O(nm))

    code

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T> inline void read(T &FF) {
    	int RR = 1; FF = 0; char CH = getchar();
    	for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
    	for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
    	FF *= RR;
    }
    inline void file(string str) {
    	freopen((str + ".in").c_str(), "r", stdin);
    	freopen((str + ".out").c_str(), "w", stdout);
    }
    const int N = 3e6 + 10;
    int n, m, now, fst[N], nxt[N], num[N], wi[N], vi[N];
    void add(int u, int v, int w) {
    	nxt[++now] = fst[u], fst[u] = now, num[now] = v, wi[now] = w;
    }
    void Add_edge(int u, int v, int k) {
    	add(u, v, 0), add(n + u, n + v, 0), add(n * 2 + u, n * 2 + v, 0);
    	if(k == 2) add(v, u, 0), add(n + v, n + u, 0), add(n * 2 + v, n * 2 + u, 0);
    }
    int vis[N], lt[N];
    void Spfa() {
    	memset(lt, 0x80, sizeof(lt));
    	queue<int> qi; qi.push(1); vis[1] = true, lt[1] = 0;
    	while(!qi.empty()) {
    		int pi = qi.front(); qi.pop(); vis[pi] = false;
    		for(int i = fst[pi]; i; i = nxt[i])
    			if(lt[pi] + wi[i] > lt[num[i]]) {
    				lt[num[i]] = lt[pi] + wi[i];
    				if(!vis[num[i]]) qi.push(num[i]), vis[num[i]] = true;
    			}
    	}
    }
    int main() {
    	//file("");
    	int u, v, k;
    	read(n), read(m);
    	for(int i = 1; i <= n; i++) read(vi[i]);
    	for(int i = 1; i <= m; i++)
    		read(u), read(v), read(k), Add_edge(u, v, k);
    	for(int i = 1; i <= n; i++) add(i, i + n, -vi[i]), add(n + i, n * 2 + i, vi[i]);
    	n = n * 3; Spfa();
    	cout << max(lt[n], 0) << endl;
    	return 0;
    }
    
  • 相关阅读:
    c++ 从vector扩容看noexcept应用场景
    c++11-17 模板核心知识(十一)—— 编写泛型库需要的基本技术
    动态链接的PLT与GOT
    c++11-17 模板核心知识(十)—— 区分万能引用(universal references)和右值引用
    Golang性能分析与优化
    c++11-17 模板核心知识(九)—— 理解decltype与decltype(auto)
    [LuoguP4808][CCC 2018]平衡树(数论分块+记忆化搜索)(有复杂度证明)
    [NOI2016]区间(线段树+尺取法)
    [BZOJ4316]小C的独立集(仙人掌+树形DP)
    [CTSC2002]灭鼠行动(模拟)
  • 原文地址:https://www.cnblogs.com/magicduck/p/12240063.html
Copyright © 2020-2023  润新知