【最短路+区间枚举】昂贵的聘礼 POJ - 1062
昂贵的聘礼 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;
}