【UVA10537】最短路 dijkstra算法

原题链接
题目大意:点有城镇和村庄两种,经过城镇时收取身上百分之五的货物的过路费(向上取整),经过村庄时只收取一个单位的过路费,问从某一点出发且到达指定终点时,身上至少携带指定数量的货物,问出发时至少带多少货物。
思路:从终点开始倒推,村庄比较好办,城镇比较麻烦,要解一个含整除的方程,博主使用的是二分法……如果有更好的方法还请指教。详情见代码。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define clr(a,b) memset(a,b,sizeof(a))
#define min(a,b) (a<b?a:b)
const int ss=60;
using namespace std;
typedef long long LL;
char ch[ss];
struct EDGE{
    int u,v,next;
}edge[ss*ss];
//得到某一字母的编号
int idx(char c){
    if(c>='A'&&c<='Z')return c-'A';
    return c-'a'+26;
}
int head[ss],pp;
void addedge(char a,char b){
    int u=idx(a),v=idx(b);
    edge[++pp]=(EDGE){u,v,head[u]};
    head[u]=pp;
}
//某点是否为城镇
bool ist(int u){
    return u<26;
}
bool vis[ss];
int g[ss];
LL d[ss];
struct heap{
    int id;
    LL d;
    bool operator<(const heap&a)const{
        return d>a.d;
    }
};
priority_queue<heap>Q;
void push(int a,LL b){
    Q.push((heap){a,b});
}
//解方程 x-((x-1)/20+1)=a,“/”为整除,且如果不是唯一解要取较小解。
LL solve(LL a){
    a++;
    LL l=0,r=2*a,ret;
    while(l<=r){
        LL m=l+(r-l)/2;
        LL fx=m-(m-1)/20;
        if(fx>=a)r=m-1,ret=m;
        else l=m+1;     
    }
    return ret;
}
//输出路径
void print(int u){
    if(u==-1)return;
    printf("-%c",ch[u]);
    print(g[u]);    
}
void dijkstra(int s,int t,LL tar){
    clr(vis,0);
    clr(g,-1);
    clr(d,100);
    d[s]=tar;
    push(s,d[s]);
    while(!Q.empty()){
        heap r=Q.top();Q.pop();
        int u=r.id;
        if(vis[u])continue;
        vis[u]=1;
        if(ist(u)){
            for(int i=head[u];i;i=edge[i].next){
                int v=edge[i].v;
                LL a=solve(d[u]);
                if(d[v]==a){
                    g[v]=min(g[v],u);
                }
                if(d[v]>a){
                    d[v]=a;
                    push(v,d[v]);
                    g[v]=u;
                }
            }
        }else{
            for(int i=head[u];i;i=edge[i].next){
                int v=edge[i].v;
                if(d[v]==d[u]+1){
                    g[v]=min(g[v],u);
                }
                if(d[v]>d[u]+1){
                    d[v]=d[u]+1;
                    push(v,d[v]);
                    g[v]=u;
                }
            }           
        }
    }
    cout<<d[t]<<endl;
    putchar(ch[t]);
    print(g[t]);
    puts("");
}
int main(){
    freopen("10537.in","r",stdin);
    for(char c='A';c<='Z';c++){
        ch[(int)c-'A']=c;
    }
    for(char c='a';c<='z';c++){
        ch[(int)c-'a'+26]=c;
    }//为字母编号
    int m,T=0;
    for(;;){
        scanf("%d",&m);getchar();
        if(m==-1)break;
        printf("Case %d:\n",++T);
        pp=0;clr(head,0);
        while(m--){
            char a=getchar();getchar();
            char b=getchar();getchar();
            addedge(a,b);
            addedge(b,a);
        }
        int t;
        scanf("%d",&t);getchar();
        char a=getchar();getchar();
        char b=getchar();getchar();
        dijkstra(idx(b),idx(a),t);
    }
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值