【最短路+區間列舉】昂貴的聘禮 POJ - 1062
阿新 • • 發佈:2020-07-29
昂貴的聘禮 POJ - 1062
題意:
原題幹說得不清不楚的……坑死我了。
- 探險家要得到物品\(i\),方式有兩種:一、花費金幣\(P[i]\)直接買;二、先得到指定物品\(X\),然後可以優惠價格\(V\)買得。
- 每個物品都有地位等級。
- 給定地位等級差距限制\(M\),表示可行的交易序列中最高地位等級與最低地位等級之差應≤\(M\),超出此範圍時不可交易。如\(M=2\)時,若要買到地位等級為\(4\)的物品,則交易序列中的物品等級只能為以下範圍:\(\{2,3,4\}或\{3,4,5\}或\{4,5,6\}\)。
- 探險家的最終目的是得到物品\(1\)。問他要花費的最少金幣數。
- 多組資料輸入
思路:
把每個物品視為圖的節點。通過物品\(X\)以優惠價格\(V\)買到物品\(i\),可視為從節點\(X\)出發,連線到節點\(i\),邊權為\(V\)的單向邊。原題目即最短路問題。
在選定起點求最短路時,注意以下幾點:
1.起點本身是要花錢的,而且這個價格不應該被最短路計算改變。
2.選定起點後求得的最短路所經過的所有節點,其地位等級都應滿足差距限制M。
3.物品1是可以直接花錢買的,其他起點求出的最短路要和這個價格再比較一下。
要實現第2點,最簡單的就是從物品\(1\)的地位等級出發,列舉可行的等級範圍。再從這個等級範圍裡列舉所有可行起點計算最短路,並且注意最短路里經過的所有節點都要滿足這個等級範圍。
資料範圍小,dijkstra、SPFA、floyd全都可以過,這裡用最簡潔的floyd。
const int INF = 1e9; const int maxn = 100 + 10; struct node { int n; int level; }; int d[maxn][maxn]; int d_copy[maxn][maxn]; int n, m; int up, down; int level[maxn]; node Node[maxn]; void floyd(int begin,int end) { for (int k = 1; k <= n; k++) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (i == j) continue; //d[i][i]是直接買物品i的價格,這是固定的,不能改變 if (d_copy[i][k] + d_copy[k][j] < d_copy[i][j]) { if (level[i]<begin || level[i]>end) continue; if (level[j]<begin || level[j]>end) continue; if (level[k]<begin || level[k]>end) continue; //i,j,k節點都要在等級範圍內,才能鬆弛 d_copy[i][j] = d_copy[i][k] + d_copy[k][j]; } } } } } int main() { // ios::sync_with_stdio(false); /// int t; cin >> t; while (t--) { int limit; //這是一個可滑動的視窗,不是以酋長等級為中位數的固定範圍…… while (cin >> limit >> n) { int ans = INF; int pos = 0; int L = 0; memset(level, 0, sizeof(level)); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { d[i][j] = INF; d_copy[i][j] = INF; } } for (int i = 1; i <= n; i++) { int value, lev, sub; cin >> value >> lev >> sub; d[i][i]=d_copy[i][i] = value; if (i == 1) L = lev; node t; t.n = i; t.level = lev; Node[++pos] = t; level[i] = lev; for (int j = 1; j <= sub; j++) { int from, dis; cin >> from >> dis; d[from][i] = d_copy[from][i]= dis; } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (d[i][j] < INF && abs(level[i] - level[j])>limit) { d[i][j] = d_copy[i][j] = INF; } } } up = L + limit; down = L - limit; if (down < 0) down = 0; //題目裡說了地位等級都是非負整數 //列舉等級區間 for (int i = 0; i+down <= L; i++) { int begin = down + i; int end = begin + limit; //確定等級下限和等級下限 floyd(begin, end); //據此計算最短路,注意i,j,k均要在此等級範圍內(尤其是k!不要忽略了) for (int j = 2; j <= n; j++) { if (level[j]<begin || level[j]>end) continue; ans = min(d_copy[j][j]+d_copy[j][1], ans); } //列舉起點的時候也要在等級範圍內 //不是列舉等級啊!!一開始寫成了(int j=begin;j<=end;j++) 太弱智了 //記得加上買起點的費用!! for (int j = 1; j <= n; j++) { for (int k = 1; k <= n; k++) { d_copy[j][k] = d[j][k]; } } //一定要把算出來的最短路清空掉!!還原回去!!才能計算下一個區間!! } ans = min(d[1][1], ans); //一定要把最後的答案再與直接買酋長禮物的價格再比較一下!!有時候直接買會更便宜 cout << ans << endl; // } } return 0; }