题解 P1073 【最优贸易】
介绍一种暴力地不能再暴力的算法
分层图大法好!!!
分层图,就是在图上有一些不可逆操作,或循环操作时,把一个点拆成多个点,以表示不同状态的方法。(自己yy的定义,dalao勿喷)
举个栗子,在一张有图上跑最短路,可逆逆行一次。
或者没走一条路,所有路的权值就变成相反数
对于这题,有三个状态
(1) 还没买水晶球
(2) 买了但是没有卖
(3) 已经卖了
那么有些人可能会说第三个状态不必要,但是有个条件,那就是必须在n号点结束。所以不一定卖掉了就万事大吉,有可能为了卖高价跑到了一个到不了n的点,这种情况我们要舍弃。
我将一个点的三层分别设为x, x + n, x + 2 * n。我认为分层比较好理解
就是这样,跑一个SPFA就行啦。
具体的在代码里啦(代码写法可能比较奇怪,大概看看吧)
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
int p[100005];
vector <pair <int, int> > V[300005];
bool inq[300005];
int d[300005];
queue <int> q;
int main(int argc, char **argv)
{
int n, m, i;
int x, y, z;
int t;
pair <int, int> r;
scanf("%d%d", &n, &m);
for(i = 1; i <= n; i++)
scanf("%d", &p[i]);
for(i = 1; i <= m; i++)
{
scanf("%d%d%d", &x, &y, &z);
V[x].push_back(make_pair(y, 0));//第一层间移动
V[x].push_back(make_pair(y + n, -p[x]));//买了x的水晶球
V[x + n].push_back(make_pair(y + n, 0));//第二层间移动
V[x + n].push_back(make_pair(y + 2 * n, p[x]));//在x卖了水晶球
V[x + 2 * n].push_back(make_pair(y + 2 * n, 0));//第三层间移动
if(z == 2)//双向边
{
V[y].push_back(make_pair(x, 0));
V[y].push_back(make_pair(x + n, -p[y]));
V[y + n].push_back(make_pair(x + n, 0));
V[y + n].push_back(make_pair(x + 2 * n, p[y]));
V[y + 2 * n].push_back(make_pair(x + 2 * n, 0));
}
}
for(i = 1; i <= 3 * n; i++)
d[i] = -1e9;
d[1] = 0;
inq[1] = 1;
q.push(1);
while(q.size())
{
t = q.front();
inq[t] = 0;
q.pop();
for(i = 0; i < V[t].size(); i++)
{
r = V[t][i];
if(d[r.first] < d[t] + r.second)
{
d[r.first] = d[t] + r.second;
if(!inq[r.first])
{
inq[r.first] = 1;
q.push(r.first);
}
}
}
}
cout << max(d[3 * n], 0) << endl;
return 0;
}
最后说一句,在极限情况下,这张图有可能是300000个点,5000000条边的,正常最短路是不可能跑过去的。但是SPFA的特点就是,效率与图的形式有很大的关系,这个图大多数边都是0,并且比较有规律,所以能过。(还是感觉数据比较水)
这个最大一点200多ms,共900多ms。