【UVA10537】The Toll! Revisited

题意

给定图 G=VE V 中有两类点,一类点(A类)在进入时要缴纳1的费用,另一类点( B 类)在进入时要缴纳当前携带金额的120(不足20的部分按20算)
  已知起点为 S ,终点为T,希望在到达 T 时能够拥有P的金额,问一开始在 S 最少要携带多少金额,并求出路径(若有多条,输出字典序最小的)
  从S离开时不需要缴费,进入 T 时需要缴费

解法

spfa变异最短路:
  其实这道题的难点就在于怎么求出花费,其他的过程则和正常的 spfa 一模一样,至于输出路径就记一个 Pre 即可
  考虑一条从 T S的路径,假设当前点为 u ,要前往的点为v,那么按 u 的种类分情况讨论:
  如果u A 类点,那么从u前往 v 的花费显然是disu+1
  如果 u B类点,那么从 u 前往v花费就是: x=disu2019whilexx+1920disux ++,最后的 x 就是从u v 的花费,不过下面的代码不是这么写的,比较鬼畜……
  然后倒着做spfa即可

复杂度

O( |V||E|

代码

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<queue>
#define Rint register int
#define Lint long long int
using namespace std;
const Lint INF=1e18;
const int E=50010;
const int N=1010;
struct node
{
    int next,from,to;
}t[E];
int head[N],num;
int Pre[N],vis[N];
Lint dis[N];
int n,S,T;
Lint p;
queue<int> q;
void add(int u,int v)
{
    t[++num]=(node){ head[u],u,v };
    head[u]=num;
}
Lint cal(Lint w)
{
    Lint u,num,y;
    u=w%20;
    if( !u )   u=w/20;
    else   u=w/20+1;
    num=u*20;
    while( w+u>num )
    {
        y=(w+u)%20;
        u= !y ? (w+u)/20 : (w+u)/20+1 ;
        num=u*20;
    }
    return u;
}
void spfa()
{
    int tmp;
    for(int i=1;i<=120;i++)   dis[i]=INF,Pre[i]=vis[i]=0;
    dis[T]=p,q.push( T );
    while( !q.empty() )
    {
        tmp=q.front(),q.pop();
        vis[tmp]=0;
        for(int i=head[tmp],x; i ;i=t[i].next)
        {
            x=t[i].to;
            if( tmp>=27 && ( dis[x]>dis[tmp]+1 || ( dis[x]==dis[tmp]+1 && t[Pre[x]].from>tmp ) ) )
            {
                dis[x]=dis[tmp]+1;
                Pre[x]=i;
                if( !vis[x] )   vis[x]=1,q.push( x );
            }
            if( tmp<=26 && ( dis[x]>dis[tmp]+cal( dis[tmp] ) || ( dis[x]==dis[tmp]+cal( dis[tmp] ) && t[Pre[x]].from>tmp ) ) )
            {
                dis[x]=dis[tmp]+cal( dis[tmp] );
                Pre[x]=i;
                if( !vis[x] )   vis[x]=1,q.push( x );
            }
        }
    }
    printf("%lld\n",dis[S]);
    printf("%c",S-1+'A');
    for(int i=t[Pre[S]].from; i ;i=t[Pre[i]].from)   printf("-%c",i-1+'A');
    printf("\n");
}
int main()
{
    char a[10],b[10];
    int u,v,C=0;
    while( scanf("%d",&n)!=EOF )
    {
        if( n==-1 )   break ;
        num=0;
        memset( head,0x0,sizeof head );
        for(int i=1;i<=n;i++)
        {
            scanf("%s%s",a,b);
            u=a[0]-'A'+1,v=b[0]-'A'+1;
            add( u,v ),add( v,u );
        }
        scanf("%lld%s%s",&p,a,b);
        S=a[0]-'A'+1,T=b[0]-'A'+1;
        printf("Case %d:\n",++C);
        spfa();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值