UVA 10537 The Toll! Revisited(dijkstra+字典序路径输出+数学公式或二分答案)

题目链接
题目大意:小写字母代表村庄,大写字母代表城镇,进入村庄需要缴纳一单位货物,进入城镇每二十个货物需要缴纳一个,问从起点到终点缴纳货物最少的路线是哪个,最小需要在起点准备多少货物,并输出这个路线,有多个路线时输出字典序最小的那个。
分析:进入一个点需要的花费可以看作边权,要求起点货物最小,那么可以从终点开始逆推,按照最短路前进,同时在松弛操作的同时记录下字典序最小的那个,然后将该结点入队,看看是否能扩展出一条最短路来。
进入城镇所需的花费需要计算一下,说繁琐也不繁琐,就是一个公式的推导,不想推也可以直接二分,也是满足单调性的。
假设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]=20d[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值