这段时间要沉迷刷题一段时间了,就让CSDN陪我一起吧!
一、题目大意
这个题目可以说是很经典的最短路问题了,就我在刷图论题的时候,已经第三次碰到这个题了,你们说它经典不经典!
题目的背景我就不在这里赘述了,总之可以抽象为一个图论问题,即每个节点代表一件物品,其具有价格和地位这两个节点属性;每条边代表替代关系,比如有一条从 i 指向 j 的边,就代表物品 i 可以由物品 j 加上该边对应权值的金币数来获得。题目要求探险家可能娶得酋长女儿所需最少的金币数,先不考虑地位问题,定义一条物品替代路径的价值为路径上边的权值加上路径终点节点价格,那么就题目转化为求解一条物品替代路径,使得其价值为所有路径中价值最小的。
二、题目思路及AC代码
上述已经说到,要求一条价值最小的路径,很明显是最短路问题。但与常规最短路问题不一样的地方就是,题目追加了一个地位的限制条件,‘地位差距超过M,则不能够交易(包括间接交易)’,那么我们可以这样考虑,因为题目确定酋长物品一定编号是1,所以我们可以以酋长的地位为基准进行遍历。这么说可能有点抽象,举个例子:
设酋长的地位为L,地位限制为M,因为它们都是整数,所以你在求最短路之前,先对节点进行过滤,把符合地位限制的节点标记出来,再在这些节点和其相关路径中求最小价值路径即可。对于上述假设,首先,将地位处于[L-M, L]的点筛选出来,这些点肯定满足地位差距都小于M,对它们求解最短路径;再将地位处于[L-M+1, L+1]的点筛选出来,求解最短路径;这样一直求解到[L, L+M],因为你已知一个地位为L,差距为M,那么所有可能的区间就是[L-M, L]到[L, L+M],上述操作也就是把所有地位限制是M的可能都遍历求解一遍,再在这些最短路中挑最小的,就是最终的结果。下面给出AC代码:
Dijkstra:
#include <iostream>
#define MAXN 101
#define INF 10000000
using namespace std;
typedef long long ll;
int M, N;
ll edges[MAXN][MAXN]; // 邻接矩阵
ll dist[MAXN];
bool vis[MAXN];
ll price[MAXN]; // 各个物品的价格
int level[MAXN];
bool can_change[MAXN]; // 用来标记某物品是否满足等级要求
void init() {
for (int i = 0; i < MAXN; i++) {
for (int j = 0; j < MAXN; j++) {
if (i == j) edges[i][j] = 0;
else edges[i][j] = INF;
}
dist[i] = INF;
vis[i] = false;
price[i] = INF;
can_change[i] = false;
}
}
ll dijkstra(int s) {
for (int i = 1; i <= N; i++) {
dist[i] = INF;
vis[i] = false;
}
dist[s] = 0;
for (int i = 1; i <= N; i++) {
ll min = INF; int x = -1;
for (int j = 1; j <= N; j++) {
if (!vis[j] && dist[j] < min && can_change[j]) {
min = dist[x = j];
}
}
if (x == -1) break;
vis[x] = true;
for (int j = 1; j <= N; j++) {
if (!vis[j] && dist[j] > dist[x] + edges[x][j] && can_change[j])
dist[j] = dist[x] + edges[x][j];
}
}
ll ans = INF;
for (int i = 1; i <= N; i++) {
if (can_change[i] && ans > dist[i] + price[i]) {
ans = dist[i] + price[i];
}
}
return ans;
}
int main()
{
init();
scanf("%d%d", &M, &N);
for (int i = 1; i <= N; i++) {
ll P, L, X;
scanf("%lld%lld%lld", &P, &L, &X);
price[i] = P;
level[i] = L;
for (int j = 0; j < X; j++) {
ll T, V;
scanf("%lld%lld", &T, &V);
edges[i][T] = V;
}
}
ll lev = level[1];
ll min = INF;
for (int i = 0; i <= M; i++) {
for (int i = 1; i <= N; i++) {
can_change[i] = false;
}
for (int j = 1; j <= N; j++) {
if (level[j] >= lev - M + i && level[j] <= lev + i) {
can_change[j] = true;
}
}
ll res = dijkstra(1);
if (min > res) {
min = res;
}
}
printf("%lld\n", min);
return 0;
}
Dijkstra + heap:
#include <iostream>
#include <queue>
#define MAXN 101
#define INF 100000000
using namespace std;
typedef pair<int, int> pp;
struct cmp {
bool operator ()(pp a, pp b) {
return a.second > b.second;
}
};
int M, N;
int edges[MAXN][MAXN];
int dist[MAXN];
int price[MAXN];
int level[MAXN];
bool vis[MAXN];
bool can_change[MAXN];
void init() {
for (int i = 0; i < MAXN; i++) {
for (int j = 0; j < MAXN; j++) {
if (i == j) edges[i][j] = 0;
else edges[i][j] = INF;
}
dist[i] = INF;
price[i] = INF;
level[i] = INF;
vis[i] = false;
}
}
int Dijkstra(int s) {
for (int i = 1; i <= N; i++) {
dist[i] = INF;
vis[i] = false;
}
priority_queue<pp, vector<pp>, cmp> pq;
pq.push(make_pair(s, 0));
dist[s] = 0;
while (!pq.empty()) {
pp p = pq.top(); pq.pop();
int v = p.first;
int d = p.second;
vis[v] = true;
for (int i = 1; i <= N; i++) {
if (!vis[i] && dist[i] > dist[v] + edges[v][i] && can_change[i]) {
dist[i] = dist[v] + edges[v][i];
pq.push(make_pair(i, dist[i]));
}
}
}
int ans = INF;
for (int i = 1; i <= N; i++) {
if (can_change[i] && ans > price[i] + dist[i])
ans = price[i] + dist[i];
}
return ans;
}
int main()
{
init();
scanf("%d%d", &M, &N);
for (int i = 1; i <= N; i++) {
int P, L, X;
scanf("%d%d%d", &P, &L, &X);
price[i] = P;
level[i] = L;
for (int j = 1; j <= X; j++) {
int T, V;
scanf("%d%d", &T, &V);
edges[i][T] = V;
}
}
int min = INF;
int lev = level[1];
for (int i = 0; i <= M; i++) {
for (int j = 1; j <= N; j++) {
can_change[j] = false;
}
for (int j = 1; j <= N; j++) {
if (level[j] >= lev - M + i && level[j] <= lev + i)
can_change[j] = true;
}
int res = Dijkstra(1);
if (min > res) {
min = res;
}
}
printf("%d\n", min);
return 0;
}
因为在练习算法嘛,就把Dijkstra和堆优化的结果都实现了一遍,也可以供大家参考!
如有问题,欢迎大家指正!