【最短路+区间枚举】昂贵的聘礼 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;
}
posted @ 2020-07-29 11:34  StreamAzure  阅读(92)  评论(0编辑  收藏  举报