洛谷题单指南-最短路-P1073 [NOIP 2009 提高组] 最优贸易
原题链接:https://www.luogu.com.cn/problem/P1073
题意解读:n个城市,m条单向或者双向道路,每个城市有价格属性,从起点跑到终点过程中,求价格最高和价格最低城市价格差的最大值,要先买后卖,也就是价格低的要在价格高的前面。
解题思路:
一、动态规划
思考买和卖的分界点,前半部分低买,后半部分高卖才是最佳策略。
1、状态定义:
设dmin[i]表示从起点1到i的路径中城市的最低价,dmax[i]表示从终点n到i的路径中城市的最高价
2、状态转移:
由于可能存在环,显然无法通过迭代实现状态转移,可以通过最短路算法实现状态转移,由于都是正权边,采用Dijikstra和SPFA都可以
又因为dmin是从起点跑最短路,dmax是从终点跑最短路,因此需要正向、反向建图
对于dmin的更新,核心逻辑为:
if(d[v] > min(d[u], price[v]))
d[v] = min(d[u], price[v])
对于dmax的更新,核心逻辑为:
if(d[v] < max(d[u], price[v]))
d[v] = max(d[u], price[v])
3、初始化:
dmin初始化为正无穷,dmax初始为负无穷
4、结果:
枚举所有的点i,计算dmin[i] + dmax[i]的最大值。
100分代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 100005, M = 2000005; //注意正反建图,M边的数量
int h1[N], h2[N], e[M], ne[M], idx; //h1正向建图,h2反向建图
int price[N];
int dmin[N]; //dmin[i]表示从起点到i路径中城市最低价格
int dmax[N]; //dmax[i]表示从终点到i路径中城市最高价格
bool vis[N];
int n, m;
void add(int h[], int a, int b)
{
e[++idx] = b;
ne[idx] = h[a];
h[a] = idx;
}
void spfa(int h[], int s, int d[], int type)
{
if(type == 1) memset(d, 0x3f, sizeof(dmin)); //type=1表示求dmin
else memset(d, -0x3f, sizeof(dmax)); //type=2表示求dmax
d[s] = price[s];
queue<int> q;
q.push(s);
while(q.size())
{
int u = q.front(); q.pop();
vis[u] = false;
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(type == 1 && d[v] > min(d[u], price[v]) || type == 2 && d[v] < max(d[u], price[v]))
{
if(type == 1) d[v] = min(d[u], price[v]);
else d[v] = max(d[u], price[v]);
if(!vis[v])
{
q.push(v);
vis[v] = true;
}
}
}
}
}
int main()
{
memset(h1, -1, sizeof(h1));
memset(h2, -1, sizeof(h2));
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> price[i];
while(m--)
{
int x, y, z;
cin >> x >> y >> z;
add(h1, x, y), add(h2, y, x);
if(z == 2) add(h1, y, x), add(h2, x, y);
}
spfa(h1, 1, dmin, 1);
spfa(h2, n, dmax, 2);
int ans = 0;
for(int i = 1; i <= n; i++) ans = max(ans, dmax[i] - dmin[i]);
cout << ans;
return 0;
}
二、分层图
建立一个3层的分层图,点i在第x层的编号为i + n * x,n为每层节点个数。
第0层和第1层之间,每个点连一条到下一层该点的边,边权为负的该点价格,表示买;
第1层和第2层之间,每个点连一条到下一层该点的边,边权为正的该点价格,表示卖;
从第0层起点1开始,跑到第2层的终点n + n * 2,最长路即为答案。
注意由于有负权边,必须用SPFA算法。
100分代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 300005, M = 3200005; //注意建3层图
int h[N], e[M], w[M], ne[M], idx; //h1正向建图,h2反向建图
int price[N];
int dist[N];
bool vis[N];
int n, m;
void add(int a, int b, int c)
{
e[++idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx;
}
int spfa()
{
memset(dist, -0x3f, sizeof(dist));
dist[1] = 0;
queue<int> q;
q.push(1);
while(q.size())
{
int u = q.front(); q.pop();
vis[u] = false;
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(dist[v] < dist[u] + w[i])
{
dist[v] = dist[u] + w[i];
if(!vis[v])
{
q.push(v);
vis[v] = true;
}
}
}
}
return dist[n + n * 2];
}
int main()
{
memset(h, -1, sizeof(h));
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> price[i];
while(m--)
{
int x, y, z;
cin >> x >> y >> z;
for(int l = 0; l < 3; l++)
{
add(x + n * l, y + n * l, 0);
if(z == 2) add(y + n * l, x + n * l, 0);
}
}
for(int i = 1; i <= n; i++)
{
add(i, i + n, -price[i]); //0到1层间点相连,权值为负价格
add(i + n, i + n * 2, price[i]); //1到2层间点相连,权值为正价格
}
cout << spfa();
return 0;
}
浙公网安备 33010602011771号