地址 https://www.acwing.com/problem/content/description/905/
年轻的探险家来到了一个印第安部落里。 在那里他和酋长的女儿相爱了,于是便向酋长去求亲。 酋长要他用10000个金币作为聘礼才答应把女儿嫁给他。 探险家拿不出这么多金币,便请求酋长降低要求。 酋长说:”嗯,如果你能够替我弄到大祭司的皮袄,我可以只要8000金币。 如果你能够弄来他的水晶球,那么只要5000金币就行了。” 探险家就跑到大祭司那里,向他要求皮袄或水晶球, 大祭司要他用金币来换,或者替他弄来其他的东西,他可以降低价格。 探险家于是又跑到其他地方,其他人也提出了类似的要求,或者直接用金币换, 或者找到其他东西就可以降低价格。 不过探险家没必要用多样东西去换一样东西,因为不会得到更低的价格。 探险家现在很需要你的帮忙,让他用最少的金币娶到自己的心上人。 另外他要告诉你的是,在这个部落里,等级观念十分森严。 地位差距超过一定限制的两个人之间不会进行任何形式的直接接触,包括交易。 他是一个外来人,所以可以不受这些限制。 但是如果他和某个地位较低的人进行了交易,地位较高的的人不会再和他交易, 他们认为这样等于是间接接触,反过来也一样。 因此你需要在考虑所有的情况以后给他提供一个最好的方案。 为了方便起见,我们把所有的物品从1开始进行编号, 酋长的允诺也看作一个物品,并且编号总是1。 每个物品都有对应的价格P,主人的地位等级L,以及一系列的替代品Ti和该替代品所对应的”优惠”Vi。 如果两人地位等级差距超过了M,就不能”间接交易”。 你必须根据这些数据来计算出探险家最少需要多少金币才能娶到酋长的女儿。 输入格式 输入第一行是两个整数M,N,依次表示地位等级差距限制和物品的总数。 接下来按照编号从小到大依次给出了N个物品的描述。 每个物品的描述开头是三个非负整数P、L、X, 依次表示该物品的价格、主人的地位等级和替代品总数。 接下来X行每行包括两个整数T和V,分别表示替代品的编号和”优惠价格”。 输出格式 输出最少需要的金币数。 数据范围 1≤N≤100, 1≤P≤10000, 1≤L,M≤N, 0≤X<N 输入格式 1 4 10000 3 2 2 8000 3 5000 1000 2 1 4 200 3000 2 1 4 200 50 2 0 输出格式 5250
解答
算法1
根据题意来说 是建立一个有向图 边权值就是可交换物品时的支付金币。另新增一个虚拟起始点,到各个点的边权值就是货物的金币值。 答案就是虚拟起点到点1的最短路径。
最短路使用dijkstra spfa均可。
还要注意最短路径上的各个点的等级差值不能超过要求,所以需要使用上下限值的多次求最短路
#include <iostream> #include <vector> #include <algorithm> #include <memory.h> using namespace std; int diff, id; const int N = 200; const int INF = 0x3f3f3f3f; int level[N]; int dist[N]; bool st[N]; int g[N][N]; int dij( int down,int up) { memset(dist, 0x3f, sizeof dist); memset(st, 0, sizeof st); dist[0] = 0; for (int i = 0; i <= id; i++) { int t = -1; for (int j = 0; j <= id; j++) { if (!st[j] && (t == -1 || dist[t] > dist[j])) { t = j; } } for (int j = 1; j <= id; j++) { if(level[j] >= down && level[j] <= up) dist[j] = min(dist[j], dist[t] + g[t][j]); } st[t] = true; } //if (dist[1] == 0x3f3f3f3f) return -1; return dist[1]; } int main() { cin >> diff >> id; memset(g, 0x3f, sizeof g); for (int i = 1; i <= id; i++) { int p, l, x; cin >> p >> l >> x; level[i] = l; g[0][i] = p; for (int j = 0; j < x; j++) { int a, b; cin >> a >> b; g[a][i] = b; } } //跑最短路 获得从0开始到1的最短距离 int ret = INF; for (int i = level[1] - diff; i <= level[1] ; i++) { ret = min(ret, dij(i,i+diff)); } cout << ret << endl; return 0; }
算法2
spfa方法,注意 spfa中虚拟起点也要进入队列参与计算,所以为了避免被等级差的规则被排除在外,将 虚拟起点的等级调整为点1起点相同。
#include <iostream> #include <vector> #include <algorithm> #include <queue> #include <memory.h> using namespace std; int diff, id; const int N = 200; const int INF = 0x3f3f3f3f; int level[N]; bool st[N]; int dist[N]; vector<pair<int, int>> g[N]; int spfa(int down,int up) { memset(dist, 0x3f, sizeof dist); dist[0] = 0; queue<int> q; q.push(0); st[0] = true; while (q.size()) { int t = q.front(); q.pop(); st[t] = false; if (level[t]<down || level[t] >up) continue; for (int i = 0; i < g[t].size(); i++) { int j = g[t][i].first; int w = g[t][i].second; if (dist[j] > dist[t] + w) { dist[j] = dist[t] + w; if (!st[j]) { q.push(j); st[j] = true; } } } } return dist[1]; } int main() { cin >> diff >> id; for (int i = 1; i <= id; i++) { int p, l, x; cin >> p >> l >> x; level[i] = l; //g[0][i] = p; g[0].push_back({ i,p }); for (int j = 0; j < x; j++) { int a, b; cin >> a >> b; //g[a][i] = b; g[a].push_back({ i,b }); } } int ret = INF; level[0] = level[1]; for (int i = level[1] - diff; i <= level[1]; i++) { ret = min(ret, spfa(i, i + diff)); } cout << ret; return 0; }