poj1062 最短路径 dijkstra

题目连接:http://poj.org/problem?id=1062

Description

年轻的探险家来到了一个印第安部落里。在那里他和酋长的女儿相爱了,于是便向酋长去求亲。酋长要他用 10000 个金币作为聘礼才答应把女儿嫁给他。探险家拿不出这么多金币,便请求酋长降低要求。酋长说:"嗯,如果你能够替我弄到大祭司的皮袄,我可以只要 8000 金币。如果你能够弄来他的水晶球,那么只要 5000 金币就行了。" 探险家就跑到大祭司那里,向他要求皮袄或水晶球,大祭司要他用金币来换,或者替他弄来其他的东西,他可以降低价格。探险家于是又跑到其他地方,其他人也提出了类似的要求,或者直接用金币换,或者找到其他东西就可以降低价格。不过探险家没必要用多样东西去换一样东西,因为不会得到更低的价格。探险家现在很需要你的帮忙,让他用最少的金币娶到自己的心上人。另外他要告诉你的是,在这个部落里,等级观念十分森严。地位差距超过一定限制的两个人之间不会进行任何形式的直接接触,包括交易。他是一个外来人,所以可以不受这些限制。但是如果他和某个地位较低的人进行了交易,地位较高的的人不会再和他交易,他们认为这样等于是间接接触,反过来也一样。因此你需要在考虑所有的情况以后给他提供一个最好的方案。 
为了方便起见,我们把所有的物品从 1 开始进行编号,酋长的允诺也看作一个物品,并且编号总是 1。每个物品都有对应的价格 P,主人的地位等级 L,以及一系列的替代品 Ti 和该替代品所对应的 "优惠"Vi。如果两人地位等级差距超过了 M,就不能 "间接交易"。你必须根据这些数据来计算出探险家最少需要多少金币才能娶到酋长的女儿。 

Input

输入第一行是两个整数 M,N(1 <= N <= 100),依次表示地位等级差距限制和物品的总数。接下来按照编号从小到大依次给出了 N 个物品的描述。每个物品的描述开头是三个非负整数 P、L、X(X < N),依次表示该物品的价格、主人的地位等级和替代品总数。接下来 X 行每行包括两个整数 T 和 V,分别表示替代品的编号和 "优惠价格"。

Output

输出最少需要的金币数。
 
题目大意:
稍微有点绕,就是说目标节点原来的价格是10000,但是如果先到2号节点,再到目标节点,价格就变为8000。如果先到3号节点,再到目标节点,则价格变为5000,等等。要求 找出最低可能的价格。但是有一个 限制,每个节点都有 等级值,如果已经到达一个较低等级的节点,则无法到达较高等级的节点。或是两节点之间的等级值差距超过给定的M,则这两个节点不能相互抵达。
解题思路:
很容易看出可以抽象成图,如果有优惠则在两点之间建边(有向),然后再建立一个汇总节点,连接其他所有节点,边权为其他节点本来的价格。问题就转化成了从汇总节点求单源最短路径。
建好图之后并不能直接跑dijkstra,因为还有限制条件。本来处理限制条件的方式是记录路径,但是后来发现不可行,于是转换思路。
两节点之间等级差不能超过M这个限制条件很好处理,松弛的时候加上限制即可,但是另一个限制条件不好处理。所以,枚举每一个节点,假设当前节点是等级最低的节点,把所有其他不符合限制条件的节点全部标记,这样就避免了出现不合法的情况,每次枚举跑一遍dijkstra,最后取最小值,时间复杂度O(n^3), 可以完成。代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define MAX 105
#define INF 0x3fffffff

using namespace std;

struct Node
{
    int level,price;
} node[MAX];

int G[MAX][MAX];
bool vis[MAX]= {0};
int d[MAX];
int n,m;

void dijkstra(int s)
{
    for(int i=1; i<=n; i++)
    {
        d[i]=INF;
    }
    d[s]=0;
    for(int i=0; i<=n; i++)
    {
        int x,m=INF;
        for(int j=0; j<=n; j++)
        {
            if(d[j]<m&&!vis[j])
            {
                m=d[j];
                x=j;
            }
        }
        vis[x]=1;
        for(int j=0; j<=n; j++)
        {
            if(vis[j])continue;//细节,dijkstra算法标记该节点只会跳过该节点作为起点,并不会阻止到该节点的边松弛,但是我们定义的标记是该节点不可访问,所以必须加上此判断,否则会WA
            d[j]=min(d[j],d[x]+G[x][j]);
        }
    }
}

int main()
{
    int p,l,x,num=0;
    int res=INF;
    cin>>m>>n;
    for(int i=0; i<=n; i++)
        for(int j=0; j<=n; j++)
            G[i][j]=INF;
    for(int i=1; i<=n; i++)
    {
        scanf("%d%d%d",&p,&l,&x);
        node[i].level=l;
        node[i].price=p;
        int v,d;
        while(x--)
        {
            scanf("%d%d",&v,&d);
            G[v][i]=d;
        }
    }
    for(int i=1; i<=n; i++)
        G[0][i]=node[i].price;
    for(int i=1; i<=n; i++)
    {
        int level=node[i].level;
        for(int j=1; j<=n; j++)
        {
            if(node[j].level-level>m||node[j].level<level)
                vis[j]=1;
            else
                vis[j]=0;
        }
        vis[0]=0;
        dijkstra(0);
        res=min(res,d[1]);
    }
    cout<<res<<endl;
}

 

转载于:https://www.cnblogs.com/cryingrain/p/8721711.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值