POJ_1062 昂贵的聘礼 Dijkstra

题目连接 https://vjudge.net/problem/POJ-1062

本题为 Dijkstra 的应用


储存结构:

所有的替代品关系都可以储存在边上,酋长的承诺,应作为最终的终点“1”

另外应虚设一个结点“0”,“0”到“1->n”的n条有向边储存商品的原价


实现原理:

每一趟 Dijkstra 以枚举出来的最小等级跑一边,把“0”作为源点,最后取酋长承诺点“1”的各个 mincost[1] 值的最小值


最小等级枚举的说明:

图上一条路径可以看作整条交易链,宅整条交易的过程中高低等级悬殊不可以超过m,可以通过“枚举最小等级”的方法来实现

依次将“1->n”n个结点的等级作为最小等级,跑一边Dijkstra,沿着边向外拓展维护的时候,弧头结点等级与最小等级差超过 m,或者弧头节点等级小于最小等级,都是为边不可达


AC关键代码:

#define PP pair<int, int>
#define INF 0x3f3f3f3f
struct edge{
    int u, v;
    int to, cost;
    bool operator < (const edge &right) const {
        return cost < right.cost;
    }
};
const int maxn = 100 + 10;
int m, n;
vector<edge> G[maxn];
int mincost[maxn];
int value[maxn];
int level[maxn];

void init()
{
    cin >> m >> n;
    edge buf;
    int u, x;
    for (int i = 1; i <= n; i ++){
        scanf("%d%d%d", &value[i], &level[i], &x);
        buf.to = i;
        for (int j = 0; j < x; j ++){
            scanf("%d%d", &u, &buf.cost);
            G[u].push_back(buf);
        }
    }
    for (int i = 1; i <= n; i ++){
        buf.to = i;
        buf.cost = value[i];
        G[0].push_back(buf);
    }
}
void manage(int minlevel)
{
    priority_queue<PP, vector<PP>, greater<PP> > qq;
    qq.push(PP(0, 0));
    mem(mincost, INF);
    mincost[0] = 0;
    PP p;
    while (!qq.empty()){
        p = qq.top();
        qq.pop();
        int u = p.second;
        if (p.first > mincost[u])   continue;

        //refresh mincost
        for (int i = 0; i < G[u].size(); i ++){
            edge e = G[u][i];
            if (!(level[e.to]-minlevel>m || level[e.to]<minlevel)){ //accessibility
                if (mincost[e.to] > mincost[u]+e.cost){
                    mincost[e.to] = mincost[u]+e.cost;
                    qq.push(PP(mincost[e.to], e.to));
                }
            }
        }
    }
}
void solve()
{
    int ans = INF;
    for (int j = 1; j <= n; j ++){
        manage(level[j]);
        ans = min(ans, mincost[1]);
    }
    cout << ans << endl;
}


但是这里遇到一个费解的问题,如果正向思维,将“1”作为源点,依旧用“枚举最小等级”法,最后取所有节点最小的“mincost(最小路径长)+value(路径终点商品原价)”的话,测试结果是WA的

附WA关键代码:

const int maxn = 100 + 10;
int m, n;
vector<edge> G[maxn];
int mincost[maxn];
int value[maxn];
int level[maxn];

void init()
{
    cin >> m >> n;
    edge buf;
    int x;
    for (int i = 1; i <= n; i ++){
        scanf("%d%d%d", &value[i], &level[i], &x);
        for (int j = 0; j < x; j ++){
            scanf("%d%d", &buf.to, &buf.cost);
            G[i].push_back(buf);
        }
    }
}
void manage(int minlevel)
{
    priority_queue<PP, vector<PP>, greater<PP> > qq;
    qq.push(PP(0, 1));
    mem(mincost, INF);
    mincost[1] = 0;
    PP p;
    while (!qq.empty()){
        p = qq.top();
        qq.pop();
        int u = p.second;
        if (p.first > mincost[u])   continue;

        //refresh mincost
        for (int i = 0; i < G[u].size(); i ++){
            edge e = G[u][i];
            if (!(level[e.to]-minlevel>m || level[e.to]<minlevel)){
                if (mincost[e.to] > mincost[u]+e.cost){
                    mincost[e.to] = mincost[u]+e.cost;  
                    qq.push(PP(mincost[e.to], e.to));
                }
            }
        }
    }
}
void solve()
{
    int ans = INF;
    for (int j = 1; j <= n; j ++){
        manage(level[j]);
        for (int i = 1; i <= n; i ++){
            mincost[i] += value[i];
            ans = min(ans, mincost[i]);
        }
    }
    cout << ans << endl;
}



这里借鉴一下其他大佬的测试样例(上面的WA版代码能通过全部测试样例):

/* 
测试数据1: 
1 4 
10000 3 2 
2 8000 
3 5000 
1000 2 1 
4 200 
3000 2 1 
4 200 
50 2 0 
 
5250 
 
测试数据2: 
1 5 
10000 3 4 
2 3000 
3 2000 
4 2000 
5 9000 
8000 2 3 
3 5000 
4 2000 
5 7000 
5000 1 0 
2000 4 1 
5 1900 
50 1 0 
 
4000 

测试数据3: 
3 8 
10000 3 6 
2 3000 
3 2000 
4 2000 
5 9000 
7 1000 
8 5008 
8000 2 3 
3 5000 
4 2000 
5 7000 
5000 1 1 
6 1000 
2000 4 1 
5 1900 
50 1 0 
5000 1 1 
7 4007 
2000 4 1 
5 1900 
80 3 0 
 
2950 

测试数据4: 
1 10 
1324 0 0 
1234 0 0 
255 0 0 
67 0 0 
56 0 0 
2134 0 0 
456 0 0 
2345 0 0 
67 0 0 
6436 0 0 
 
1324 
 
测试数据5:   
///debug    一整条交易向上的等级差异都不能超过 m 
///这里帮助我纠正了第一次写的算法,当时以为等级差只是在某一条边的两端不能超过m 
1 4 
10000 3 2 
2 1 
3 3 
1000 2 2 
4 1 
3 1 
1000 3 1 
4 2 
100 4 0 
 
105 

测试数据6: 
3 5 
10000 3 4 
2 3000 
3 2000 
4 2000 
5 9000 
8000 2 3 
3 5000 
4 2000 
5 7000 
5000 1 0 
2000 4 1 
5 1900 
50 1 0 
 
3950 

测试数据7: 
0 5 
10000 3 4 
2 3000 
3 2000 
4 2000 
5 9000 
8000 2 3 
3 5000 
4 2000 
5 7000 
5000 4 0 
2000 3 1 
5 1900 
50 2 0 
 
4000 
*/  




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sycamore_Ma

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值