You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

7.4 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

AcWing 903. 昂贵的聘礼

一、题目描述

年轻的探险家来到了一个印第安部落里。

在那里他和酋长的女儿相爱了,于是便向酋长去求亲。

酋长要他用 10000 个金币作为聘礼才答应把女儿嫁给他。

探险家拿不出这么多金币,便请求酋长降低要求。

酋长说:嗯,如果你能够替我弄到大祭司的皮袄,我可以只要 8000 金币。如果你能够弄来他的水晶球,那么只要 5000 金币就行了。

探险家就跑到大祭司那里,向他要求皮袄或水晶球,大祭司要他用金币来换,或者替他弄来其他的东西,他可以降低价格。

探险家于是又跑到其他地方,其他人也提出了类似的要求,或者直接用金币换,或者找到其他东西就可以降低价格。

不过探险家没必要用多样东西去换一样东西,因为不会得到更低的价格。

探险家现在很需要你的帮忙,让他 用最少的金币娶到自己的心上人

另外他要告诉你的是,在这个部落里,等级观念十分森严。

地位差距超过一定限制的两个人之间不会进行任何形式的直接接触,包括交易。

他是一个外来人,所以可以不受这些限制。

但是如果他和某个地位较低的人进行了交易,地位较高的的人不会再和他交易,他们认为这样等于是间接接触,反过来也一样。

因此你需要在考虑所有的情况以后给他提供一个最好的方案。

为了方便起见,我们把所有的物品从 1 开始进行编号,酋长的允诺也看作一个物品,并且编号总是 1

每个物品都有对应的价格 P,主人的地位等级 L,以及一系列的替代品 T_i 和该替代品所对应的 优惠 V_i

如果两人地位等级差距超过了 M,就不能 间接交易

你必须根据这些数据来计算出探险家 最少需要多少金币才能娶到酋长的女儿

输入格式 输入第一行是两个整数 MN,依次表示地位 等级差距限制物品的总数

接下来按照编号从小到大依次给出了 N 个物品的描述。

每个物品的描述开头是三个非负整数 P、L、X,依次表示该物品的 价格、主人的 地位等级替代品总数

接下来 X 行每行包括两个整数 TV,分别表示 替代品的编号优惠价格

输出格式 输出最少需要的金币数。

二、题目解析

测试样例理解

//测试用例解释

//等级差距限制 和 物品的总数
1 4     

//1号物品3级有2个可替代品
10000 3 2  
2 8000      //替代品的编号:2,优惠价格:8000
3 5000      //替代品的编号:3,优惠价格:5000
---------------------------------------------
//2号物品2级有1个可替代品
1000 2 1   
4 200      //替代品的编号:5,优惠价格:200
---------------------------------------------
//3号物品2级有1个替代品
3000 2 1   
4 200      //替代品的编号4优惠价格:200
---------------------------------------------
//4号物品2级有0个替代品
50 2 0     

建图方式

假入我们想要A物品,而A物品的原价是w_1元,如果有B物品作为交换的话,只需要c_1元就可以得到A物品,那我们不就相当于B物品和c_1元可以得到A物品,也就是等价于BA的路径为c_1吗?

那每个物品的原价我们又该怎么处理呢?这里在建图上有一个特殊的技巧:建立一个 超级源点 O! O到每个物品的距离就是物品的原价,而我们需要不断地交换来降低我们想要获得物品的花费,这就是一个最短路问题了。

  • 每个点 i 的价格 相当于 从点0到点 i 连一条边, 边权 定义为点i的价格
  • 每个点 i 有多个可替代点: 从可替代点 到点i 连一条边
  • 结果:顶点 0 到 顶点 1最短路

等级限制

  • 酋长的女儿肯定是要娶到手的,所有的路径都会汇集在 1 号点,也就是说 1 号点是所有路径中都存在的点

  • 假设 1号点等级为 L_1,则所有最短路的点都必须满足在 [L_1-M,L_1+M] 范围内

  • 如果只是将[L_1-M,L_1+M] 这个区间作为最后的区间,会存在两个点的等级差超过了 M 值,不符合题意,所以,这个区间还要继续缩小

依次枚举区间 [L_1-M,L_1],[L_1-M+1,L_1+1],[L_1-M+2,L_1+2]...[L_1,L_1+M],这些小区间内的任意两个点的等级都不会超过 M 值,并且同时保证了 1 号点肯定在区间内。

因此,依次求出每个小区间的最短路,最后再取最小值就是答案

三、Code

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
const int M = N * N; // 边数最多有n^2,这是顶天设置此处与传统的题目不一般的M= N<<1此题目没有明确给出边数上限直接认为N^2
const int INF = 0x3f3f3f3f;

int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int c) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

int dis[N]; // 单源最短路径
bool st[N]; // 配合Dijkstra用的是否出队过

int L[N]; // 每个节点的等级
int n, m; // n个物品m表示等级差距限制

int dijkstra(int l, int r) {
    memset(dis, 0x3f, sizeof dis);
    memset(st, 0, sizeof st);
    priority_queue<PII, vector<PII>, greater<PII>> q;
    // 距离,节点号
    q.push({0, 0}); // 超级源点
    dis[0] = 0;

    while (q.size()) {
        auto t = q.top();
        q.pop();
        int u = t.second;
        if (st[u]) continue;
        st[u] = true;

        for (int i = h[u]; ~i; i = ne[i]) {
            int v = e[i];
            // 枚举边时,只处理等级在指定范围内
            if (L[v] < l || L[v] > r) continue;

            if (dis[v] > dis[u] + w[i]) {
                dis[v] = dis[u] + w[i];
                q.push({dis[v], v});
            }
        }
    }
    return dis[1];
}

int main() {
    memset(h, -1, sizeof h); // 初始化邻接表
    cin >> m >> n;           // m:表示地位等级差距限制n:物品的总数

    for (int i = 1; i <= n; i++) { // 枚举每个节点
        int p, l, cnt;             // 价格 等级 替代品数目
        cin >> p >> L[i] >> cnt;

        add(0, i, p); // 虚拟源点0, 0获取i号物品需要p这么多的金币

        while (cnt--) {    // 读入物品i的替代品
            int u, v;      // 替代品的编号 和 优惠价格
            cin >> u >> v; // u:替代品编号v:收到替代品后的收费价格
            add(u, i, v);  // 从替代品向可替代品引一条长度为v的边
        }
    }
    // 预求最小,先设最大
    int res = INF;
    // 枚举区间范围进行多次求最小路径
    for (int i = L[1] - m; i <= L[1]; i++)
        res = min(res, dijkstra(i, i + m));
    // 输出结果
    cout << res << endl;
    return 0;
}