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;
}
posted @ 2024-02-22 19:12  hh20080501hh  阅读(186)  评论(0)    收藏  举报