题目链接
题目大意:小写字母代表村庄,大写字母代表城镇,进入村庄需要缴纳一单位货物,进入城镇每二十个货物需要缴纳一个,问从起点到终点缴纳货物最少的路线是哪个,最小需要在起点准备多少货物,并输出这个路线,有多个路线时输出字典序最小的那个。
分析:进入一个点需要的花费可以看作边权,要求起点货物最小,那么可以从终点开始逆推,按照最短路前进,同时在松弛操作的同时记录下字典序最小的那个,然后将该结点入队,看看是否能扩展出一条最短路来。
进入城镇所需的花费需要计算一下,说繁琐也不繁琐,就是一个公式的推导,不想推也可以直接二分,也是满足单调性的。
假设u和v是两个城镇,从u到v的花费是
[
d
[
u
]
/
20
]
(
向
上
取
整
)
[d[u]/20](向上取整)
[d[u]/20](向上取整)在v点的最小货物为
d
[
v
]
d[v]
d[v]那么
d
[
u
]
−
[
d
[
u
]
/
20
]
=
d
[
v
]
d[u]-[d[u]/20]=d[v]
d[u]−[d[u]/20]=d[v]由于我们是逆推,所以我们是知道
d
[
v
]
d[v]
d[v]而不知道
d
[
u
]
d[u]
d[u]所以我们将这个等式变换一下
d
[
u
]
−
(
d
[
u
]
+
19
)
/
20
=
d
[
v
]
d[u]-(d[u]+19)/20=d[v]
d[u]−(d[u]+19)/20=d[v]然后得出
d
[
u
]
=
20
∗
d
[
v
]
/
19
+
1
d[u]=20*d[v]/19+1
d[u]=20∗d[v]/19+1路径的花费就是
w
=
d
[
v
]
/
19
+
1
w=d[v]/19+1
w=d[v]/19+1这时算出的
w
w
w也是向上取整的,所以我们可以加一下判断
d
[
v
]
d[v]
d[v]是否可以整除19,然后就可以了。
当然也可以选择直接二分,满足单调性,下面给出二分的代码,返回值是边的权值
LL cost(int num,LL d)
{
if(sy[num]>='a'&&sy[num]<='z') return 1;
LL l=0,r=1e18,ans,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(mid-(mid+19)/20>d) r=mid-1;
else if(mid-(mid+19)/20<d) l=mid+1;
else ans=mid,r=mid-1;
}
return (ans+19)/20;
}
完整代码
#include<bits/stdc++.h>
#define PII pair<long long,int>
#define x first
#define y second
#define MAIN main
using namespace std;
typedef long long LL;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int N=1e3+10;
struct edge
{
int v,next;
}e[N<<1];
int head[N],cnt;
void add(int u,int v)
{
e[++cnt].v=v;
e[cnt].next=head[u];
head[u]=cnt;
}
int n,m;
int f[N];
char sy[N];
int vis[N];
int pre[N];
LL dis[N];
LL cost(int num,LL d)
{
if(sy[num]>='a'&&sy[num]<='z') return 1;
LL l=0,r=1e18,ans,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(mid-(mid+19)/20>d) r=mid-1;
else if(mid-(mid+19)/20<d) l=mid+1;
else ans=mid,r=mid-1;
}
return (ans+19)/20;
}
void dijkstra(int s,int p)
{
for(int i=1;i<=n;i++) dis[i]=INF,vis[i]=0;
dis[s]=p;
priority_queue<PII>q;
q.push(make_pair(-dis[s],s));
while(!q.empty())
{
int u=q.top().y;
q.pop();
if(vis[u]) continue;
vis[u]=1;
LL w;
//w=cost(u,dis[u]);
w=dis[u]/19+(dis[u]%19==0?0:1);
if(sy[u]>='a'&&sy[u]<='z') w=1;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].v;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
pre[v]=u;
q.push(make_pair(-dis[v],v));
}
else if(dis[v]==dis[u]+w&&sy[pre[v]]>sy[u])
{
pre[v]=u;
q.push(make_pair(-dis[v],v));
}
}
}
}
void print(int u)
{
if(pre[u]!=-1)
{
printf("%c-",sy[u]);
print(pre[u]);
}
else
{
printf("%c",sy[u]);
}
}
int MAIN()
{
int tt=0;
while(scanf("%d",&m)==1)
{
if(m==-1) break;
n=0;
cnt=0;
memset(head,0,sizeof(head));
memset(pre,-1,sizeof(pre));
memset(f,0,sizeof(f));
for(int i=1;i<=m;i++){
char u[2],v[2];
scanf("%s%s",u,v);
if(!f[u[0]-'A']) f[u[0]-'A']=++n,sy[n]=u[0];
if(!f[v[0]-'A']) f[v[0]-'A']=++n,sy[n]=v[0];
add(f[u[0]-'A'],f[v[0]-'A']);
add(f[v[0]-'A'],f[u[0]-'A']);
}
char a[2],b[2];
int p;
scanf("%d%s%s",&p,a,b);
if(!f[a[0]-'A']) f[a[0]-'A']=++n,sy[n]=a[0];
if(!f[b[0]-'A']) f[b[0]-'A']=++n,sy[n]=b[0];
int s=f[a[0]-'A'];int t=f[b[0]-'A'];
dijkstra(t,p);
printf("Case %d:\n",++tt);
printf("%lld\n",dis[s]);
print(s);
printf("\n");
}
return 0;
}