题意
给定图 G=(V,E) , 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=disu∗2019,while(x−x+1920<disu)x ++,最后的 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;
}