UVA 10537 The Toll! Revisited(最短路变形+输出字典序最小路径)

题目:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1478

题目大意:要运送东西,路过一个地方要给过路费,有两种,一是村庄,给 1 个物资,二是城镇,给 当前物资数/20 的物资,不足20当20算。给你 n 条边,每条边给你两个端点,小写的表示村庄,大写的表示城镇。现在给你起点和终点,还有你要运往终点的物资,问你在起点最少需要准备多少的物资,输出来,然后打印字典序最小的路径。

解题思路:从终点逆着进行更新,用 d[ i ] 表示已经进入了 i 再从 i 到达终点需要准备的最小物资数。那么 d[ e ] 很明显就是输入的那个数。然后就是更新了,dij 和 spfa 都可以,就把所有的 d 都算出来了,然后从起点根据 d 递归打印路劲就行了。两个地方需要注意:(1)由于是d 是逆着算的,有一个地方,我纠结了很久,就是城镇那里,你知道了进入城镇后剩余的量,那怎么求进入之前的量,想了很久,公式退步出来,后来看了人家博客才发现,原来很简单,不用推公式,想想如果 x/20 不是向上取整, x - x/20 = y ,那么 x = 20*y/19,那么现在是向上取整,那么这个 x 肯定是 >= 20*y/19 。然后就是 ++ 暴力往上算凑即可,最多次数是多少,我不知道怎么算(回去后有时间再算算。。 = =),我试验了一下,这个次数很少的,对复杂度没有影响。(2)输出路径的时候,根据 d 进行输出,是递归满足 d[ u ] - w = d[ v ] 的最小的 v ,所以只要从小到大枚举 v 即可,这个w 就很好算了,这是正序的算。

  还有一点需要总结,个人原因,此题错了 n 次,最后发现有两个地方:(1)由于这道题数据范围比较大,要用 long long ,我有的中间变量就是用的 int ,还是多注意吧,以后大不了把 int 全改成 long long 算了。(2)路径打印的地方,应该是 a-b ,我竟然是 a->b ,不想多说什么了,脑残啊。。

代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

typedef long long lld;

const int MAXN = 333;
const lld INF = 0x0fffffffffffffff;

int Map[333][333];

struct Node
{
    int id;
    lld val;
    Node(int iid,lld vval)
    {
        id = iid;
        val = vval;
    }
    bool operator < (const Node& tmp) const
    {
        if(val == tmp.val)
        {
            return id < tmp.id;
        }
        else return val > tmp.val;
    }
};

priority_queue<Node> q;

lld d[MAXN];

int done[MAXN];

int is_town(int x)
{
    if(x <= 'Z' && x >= 'A') return 1;
    else return 0;
}

void dij(int load,char s,char e)
{
    for(int i = 0;i < 256;i++)
        d[i] = INF;
    d[s] = load;
    q.push(Node(s,load));
    memset(done,0,sizeof(done));
    while(!q.empty())
    {
        Node cur = q.top();
        q.pop();
        if(done[cur.id]) continue;
        done[cur.id] = 1;
        for(int i = 0;i < 256;i++)
        {
            if(Map[cur.id][i] == 0) continue;
            int next_id = i;
            lld tmp;
            if(is_town(cur.id))
            {
                for(tmp = cur.val*20/19;;tmp++)
                {
                    if(tmp-(tmp+19)/20 >= cur.val)
                        break;
                }
            }
            else tmp = cur.val+1;
            if(tmp < d[next_id])
            {
                d[next_id] = tmp;
                q.push(Node(next_id,tmp));
            }
        }
    }
}

void print(int u,int e)
{
    if(u == e)
    {
        printf("%c\n",e);
        return ;
    }
    printf("%c-",u);
    for(int i = 0;i < 256;i++)
        if(Map[u][i] == 1)
        {
            lld tmp;
            if(is_town(i)) tmp = d[u]-(d[u]+19)/20;
            else tmp = d[u]-1;
            if(d[i] == tmp)
            {
                print(i,e);
                break;
            }
        }
}

int main()
{
    int cas = 0;
    int m;
    while(~scanf("%d",&m))
    {
        if(m == -1) break;
        memset(Map,0,sizeof(Map));
        char str[5],str1[5];
        while(m--)
        {
            scanf("%s%s",str,str1);
            Map[str[0]][str1[0]] = 1;
            Map[str1[0]][str[0]] = 1;
        }
        int load;
        scanf("%d%s%s",&load,str,str1);
        dij(load,str1[0],str[0]);
        printf("Case %d:\n",++cas);
        printf("%lld\n",d[str[0]]);
        print(str[0],str1[0]);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值