P10110 [GESP202312 七级] 商品交易 题解
更新于二零二四年二月二日:给出了 dijkstra 的代码,新增了注释。
题目
分析
要从 \(a\) 物品交换到 \(b\) 物品,其中每个商人有一种交易方式,我们可以很容易想到建一个图来解决。将商品看作节点,将商人看作边,从物品 \(i\) 能换到物品 \(j\) 其实就相当于 \(i\) 和 \(j\) 中间建了一条边。那么问题就很简单了,只需要求一遍最短路即可(当然,如果你把自己赚的钱当边权求最长路也是一样的)。同时,我们可以发现,商人会很良心的帮你补差价,所以其实从起点走到终点所需要的费用就是手续费,即你通过边的数量,所以我们可以直接把边权赋值为 \(1\),而不需要计算出每个边权。这样的话,我们就可以保证边权都是正边,而且不需要担心出现负环啦!
不过我在写代码的时候没有意识到可以直接将边权赋为 \(1\),我太蒻了,所以我写的是 spfa,前面已经提到过了,不会出现负环,所以我们的 spfa 不会一直在环里面跑。当然写 dijkstra 也是没有问题滴。
代码
spfa:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10 , M = 2e5+10;
int n , m , have , need;
int h[N] , e[M] , ne[M] , w[M] , idx;
int val[N];
ll dist[N];
bool in_que[N];
void add (int a , int b , int c) //建边
{
e[idx] = b , ne[idx] = h[a] , w[idx] = c , h[a] = idx++;
}
void spfa ()
{
memset(dist , 0x3f , sizeof dist);
queue <int> q;
q.push(have);
in_que[have] = 1 , dist[have] = 0;
while (!q.empty())
{
int u = q.front();
q.pop();
in_que[u] = 0;
for (int i=h[u] ; i!=-1 ; i=ne[i])
{
int j = e[i];
if (dist[j]>dist[u]+w[i])
{
dist[j] = dist[u] + w[i];
if (!in_que[j]) //如果不在队列里面再加进去
{
in_que[j] = 1;
q.push(j);
}
if (j==need)
{
cout << dist[j]+val[need]-val[have] << endl;
exit (0); //退出程序
}
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
cin >> n >> m >> have >> need;
memset(h , -1 , sizeof h);
for (int i=0 ; i<n ; i++)
{
cin >> val[i]; //读入商品价值
}
while (m--)
{
int a , b;
cin >> a >> b;
add (a , b , 1); //转化问题,建边长为1的边
}
spfa (); //求最短路
cout << "No solution" << endl;
return 0;
}
dijkstra:
(看到说要卡 spfa 就赶紧更新了 dijkstra 的做法)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10 , M = 2e5+10;
int n , m , have , need;
int h[N] , e[M] , ne[M] , w[M] , idx;
int val[N];
ll dist[N];
bool vis[N];
struct node
{
int u;
ll dist;
bool operator <(const node &o) const //优先队列小就是大,大就是小,所以写大于号
{
return dist>o.dist;
}
};
void add (int a , int b , int c) //建边
{
e[idx] = b , ne[idx] = h[a] , w[idx] = c , h[a] = idx++;
}
void dij ()
{
memset(dist , 0x3f , sizeof dist);
priority_queue <node> q;
q.push({have , 0});
dist[have] = 0;
while (!q.empty())
{
int u = q.top().u; //取出距离最小的点
q.pop();
if (vis[u])
{
continue;
}
vis[u] = 1; //标记已经走过
for (int i=h[u] ; i!=-1 ; i=ne[i])
{
int j = e[i];
if (dist[j]>dist[u]+w[i])
{
dist[j] = dist[u] + w[i];
q.push({j , dist[j]});
}
if (j==need) //判断是否走到需要的点了
{
cout << dist[j]+val[need]-val[have] << endl;
exit (0); //退出程序
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
cin >> n >> m >> have >> need;
memset(h , -1 , sizeof h);
for (int i=0 ; i<n ; i++)
{
cin >> val[i]; //读入商品价值
}
while (m--)
{
int a , b;
cin >> a >> b;
add (a , b , 1); //转化问题,建边长为1的边
}
dij (); //求最短路
cout << "No solution" << endl;
return 0;
}

浙公网安备 33010602011771号