题解【POJ1062】昂贵的聘礼

题面

比较复杂的最短路模型转换。

我们考虑一种建图方式:

  • 建立一个超级源点 \(S\),它向每一个节点连一条权值为那一个节点物品价值的边,表示直接购买那一个物品;
  • 对于每一个节点,由它每一个可用的替代品向它连一条权值为当前替代品“优惠价格”的边,表示使用那一个替代品来购买当前物品。
  • 最终答案即为 \(S\)\(1\) 号节点的最短距离。

考虑等级限制,我们可以枚举当前最低的等级,然后在进行松弛操作的时候判断一下当前节点是否在当前等级限制之内。每一次跑完最短路后将答案与当前的 \(dist_1\)\(\text{min}\)

注意从 \(0\) 开始枚举最低等级。

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

const int N = 103, M = 20003;

int n, m;
int grade[N]; //每个物品的等级
int tot, head[N], ver[M], nxt[M], edge[M];
int dist[N], vis[N];
int S;

inline void add(int u, int v, int w)
{
    ver[++tot] = v, edge[tot] = w, nxt[tot] = head[u], head[u] = tot;
}

inline void Dij(int s/*起点*/, int t/*终点*/, int LowGrade/*当前的最低等级*/, int HighGrade/*当前的最高等级*/)
{
    memset(dist, 0x3f, sizeof dist);
    priority_queue <pair <int, int> > q;
    dist[S] = 0;
    memset(vis, 0, sizeof vis);
    q.push(make_pair(0, S));
    while (!q.empty())
    {
        int u = q.top().second; q.pop();
        vis[u] = 0;
        for (int i = head[u]; i; i = nxt[i])
        {
            int v = ver[i], w = edge[i];
            if (grade[v] < LowGrade || grade[v] > HighGrade) continue; //不满足当前的等级限制
            if (dist[v] > dist[u] + w)
            {
                dist[v] = dist[u] + w;
                if (!vis[v])
                    vis[v] = 1,
                    q.push(make_pair(-dist[v], v));
            }
        }
    }
}

int main()
{
    cin >> m >> n;
    S = n + 1; //超级源点
    for (int i = 1; i <= n; i+=1)
    {
        int u = i, v, w, x;
        cin >> w >> grade[i] >> x;
        add(S, u, w); //向超级源点连一条权值为物品价格的边
        for (int j = 1; j <= x; j+=1)
            cin >> v >> w,
            add(v, u, w); //由替代品向当前物品连边
    }
    int ans = 2000000007;
    for (int nowLowGrade = 0; nowLowGrade <= n; nowLowGrade+=1) //枚举最低等级,注意要从 0 开始
    {
        Dij(S, 1, nowLowGrade, nowLowGrade + m); //跑一次 Dijkstra 最短路
        ans = min(ans, dist[1]); //将答案取最小值
    }
    cout << ans << endl;
    return 0;
}
posted @ 2020-02-28 11:53  csxsi  阅读(121)  评论(0编辑  收藏  举报