【ACWing】903. 昂贵的聘礼

题目地址:

https://www.acwing.com/problem/content/905/

年轻的探险家来到了一个印第安部落里。在那里他和酋长的女儿相爱了,于是便向酋长去求亲。酋长要他用 10000 10000 10000个金币作为聘礼才答应把女儿嫁给他。探险家拿不出这么多金币,便请求酋长降低要求。酋长说:”嗯,如果你能够替我弄到大祭司的皮袄,我可以只要 8000 8000 8000金币。如果你能够弄来他的水晶球,那么只要 5000 5000 5000金币就行了。”探险家就跑到大祭司那里,向他要求皮袄或水晶球,大祭司要他用金币来换,或者替他弄来其他的东西,他可以降低价格。探险家于是又跑到其他地方,其他人也提出了类似的要求,或者直接用金币换,或者找到其他东西就可以降低价格。不过探险家没必要用多样东西去换一样东西,因为不会得到更低的价格。探险家现在很需要你的帮忙,让他用最少的金币娶到自己的心上人。另外他要告诉你的是,在这个部落里,等级观念十分森严。地位差距超过一定限制的两个人之间不会进行任何形式的直接接触,包括交易。他是一个外来人,所以可以不受这些限制。但是如果他和某个地位较低的人进行了交易,地位较高的的人不会再和他交易,他们认为这样等于是间接接触,反过来也一样。因此你需要在考虑所有的情况以后给他提供一个最好的方案。为了方便起见,我们把所有的物品从 1 1 1开始进行编号,酋长的允诺也看作一个物品,并且编号总是 1 1 1。每个物品都有对应的价格 P P P,主人的地位等级 L L L,以及一系列的替代品 T i T_i Ti和该替代品所对应的”优惠” V i V_i Vi。如果两人地位等级差距超过了 M M M,就不能”间接交易”。你必须根据这些数据来计算出探险家最少需要多少金币才能娶到酋长的女儿。

输入格式:
输入第一行是两个整数 M M M N N N,依次表示地位等级差距限制和物品的总数。接下来按照编号从小到大依次给出了 N N N个物品的描述。每个物品的描述开头是三个非负整数 P P P L L L X X X,依次表示该物品的价格、主人的地位等级和替代品总数。接下来 X X X行每行包括两个整数 T T T V V V,分别表示替代品的编号和”优惠价格”。

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

数据范围:
1 ≤ N ≤ 100 1≤N≤100 1N100
1 ≤ P ≤ 10000 1≤P≤10000 1P10000
1 ≤ L , M ≤ N 1≤L,M≤N 1L,MN
0 ≤ X < N 0≤X<N 0X<N

可以把每个物品看成是一个图上的顶点,初始状态(即两手空空的状态)看成是起点,从起点出发,每次要进行买入操作,就视为在图上从某个顶点到另一个顶点的移动,这里的“买入”可以指单纯用金钱买,也可以指拿着手里的东西加上钱去换。当一个物品 x x x加上钱 a a a能换另一个物品 y y y的时候,我们就建立一条有向边 x → y x\to y xy,其权为 a a a。而初始状态要向每个物品都连一条边,权就是该物品纯用钱买是多少钱。这样从初始状态到终点的任意一条路径就对应着一种交易方式,反之亦然。问题就转化为了求单源最短路。但是这里还需要注意等级差,所以在求最短路的时候,我们还需要枚举路径上的物品的等级所在的范围。如果给定的等级差是 M M M,并且酋长的“允诺”的等级是 L L L,那么我们就要枚举 [ L − M , L ] [L-M,L] [LM,L]一直到 [ L , L + M ] [L, L+M] [L,L+M]这些范围的等级区间,看交易哪个区间的物品的花费最小即可。最短路可以用朴素版Dijkstra算法来做。代码如下:

#include <iostream>
#include <cstring>
using namespace std;

const int N = 110, INF = 0x3f3f3f3f;
int n, m;
// g[i][j]表示从物品i到物品j的花费(即物品i再加多少钱能换物品j);
// 0代表初始状态,即g[0][i]代表物品i用钱买是多少钱
int g[N][N], level[N];
int dist[N];
bool st[N];

// [l, r]表示等级区间
int dijkstra(int l, int r) {
    
    
    memset(dist, 0x3f, sizeof dist);
    memset(st, 0, sizeof st);
    dist[0] = 0;

    for (int i = 0; i < n + 1; i++) {
    
    
    	// 找到在等级区间中的、未算出最短路的并且路径最短的那个点
        int t = -1;
        for (int j = 0; j <= n; j++)
            if (!st[j] && (j == 0 || (l <= level[j] && level[j] <= r)) && (t == -1 || dist[t] > dist[j]))
                t = j;
    
    	// 标记其为已算出
        st[t] = true;
        // 以t更新其所有未算出过的、且在等级区间中的邻接点
        for (int j = 1; j <= n; j++)
            if (!st[j] && l <= level[j] && level[j] <= r)
                dist[j] = min(dist[j], dist[t] + g[t][j]);
    }
	
	// 酋长的允诺是第1个物品
    return dist[1];
}

int main() {
    
    
    cin >> m >> n;

    memset(g, 0x3f, sizeof g);

    for (int i = 1; i <= n; i++) {
    
    
        int price, cnt;
        cin >> price >> level[i] >> cnt;
        g[0][i] = min(g[0][i], price);
        for (int j = 0; j < cnt; j++) {
    
    
            int id, cost;
            cin >> id >> cost;
            g[id][i] = min(g[id][i], cost);
        }
    }

    int res = INF;
    // 枚举等级区间的左端点,更新res得最小花费
    for (int i = level[1] - m; i <= level[1]; i++)
        res = min(res, dijkstra(i, i + m));
    
    cout << res << endl;

    return 0;
}

时间复杂度 O ( M N 2 ) O(MN^2) O(MN2),空间 O ( N 2 ) O(N^2) O(N2)

猜你喜欢

转载自blog.csdn.net/qq_46105170/article/details/115121657