题目链接
题意
n个人每人有一个物品,每个物品可以直接买,也可以通过其他物品抵押加上少量的钱买,每个人有一个等级,经过若干次交换购买的人中的最大等级差不能超过m。求要得到第一件物品的最少花费是多少?
思路
如果没有等级约束就是一个最短路的模板题,根据a物品加w个金币换到b物品,可以建立一条有向边a->b,cost w。然后直接用最短路即可,但是因为有了约束,不能直接调用。考虑题目的要求是要换到编号为1的物品,所以能交换到1的人群中,等级差异不能超过m,并且这个等级范围要包括1的等级,因题目范围较小,直接枚举区间,多次调用dijkstra取最小值。
还有一个小技巧,题中没有规定起点,我们可以假设0为起点,从0到每个顶点建立一条边,边的权值就是直接购买该物品的价格。
#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define pr pair<int, int>
const int maxn = 1e5 + 7;
typedef long long ll;
int head[maxn], to[maxn<<1], nex[maxn<<1], edge[maxn<<1], cnt;
int n, m;
void init() {
memset(head, -1, sizeof(head));
cnt = 0;
}
void add(int x, int y, int z) {
to[cnt] = y;
edge[cnt] = z;
nex[cnt] = head[x];
head[x] = cnt++;
}
int d[maxn], p[maxn];
bool final[maxn];
void dijkstra(int l, int r)
{
memset(final, 0, sizeof(final));
memset(d, INF, sizeof(d));
priority_queue<pr, vector<pr>, greater<pr> > q;
q.push(make_pair(0, 0));//first是length,second是顶点
d[0] = 0;
while (!q.empty())
{
int u = q.top().second;
q.pop();
final[u] = true;
for (int i = head[u]; ~i; i = nex[i])
{
int v = to[i];
int len = edge[i];
if(!final[v] && d[v] > d[u] + len && p[v] >= l && p[v] <= r)
{
d[v] = d[u] + len;
q.push(make_pair(d[v], v));
}
}
}
}
int main()
{
init();
scanf("%d %d", &m, &n);
for (int i = 1; i <= n; i++) {
int x;
scanf("%d%d%d", &d[i], &p[i], &x);
for (int j = 1; j <= x; j++) {
int a, b;
scanf("%d%d", &a, &b);
add(a, i, b);
}
}
for (int i = 1; i <= n; i++)
add(0, i, d[i]);
p[0] = p[1];
int ans = INF;
for (int i = max(0, p[1] - m); i - p[1] <= m; i++) {
dijkstra(i, min(i + m, p[1] + m));
ans = min(d[1], ans);
}
printf("%d\n", ans);
}