题目链接:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&category=0&problem=1478&mosmsg=Submission+received+with+ID+26584073
每进入村庄或城镇都要收费,已知到要运送到终点的费用,很自然地想到从终点向起点逆推。
逆推的过程类似 (dijstra),每次选出已知收费最少的点进行扩展,重点在于路径收费的计算,可以用数学推式子,也可以二分,最后从起点向后输出字典序最小的路径即可,在路径上的点满足 (d[v] == d[u] + w)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, int> pii;
const int maxn = 105;
const ll INF = 1ll << 60ll;
int n, m;
vector<int> G[maxn];
ll P;
ll g[maxn][maxn];
int ID(char ch){
if(ch >= 'A' && ch <= 'Z') return ch - 'A' + 1;
else return 26 + ch - 'a' + 1;
}
ll get(ll left){
ll L = left, R = INF;
ll res;
while(L <= R){
ll mid = (L + R) >> 1;
ll fre = mid / 20ll;
if(mid % 20ll != 0) ++fre;
if(mid - fre >= left){
R = mid - 1ll;
res = mid;
} else if(mid - fre < left){
L = mid + 1ll;
}
}
return res - left;
}
ll d[maxn];
void dij(int S){
priority_queue<pii, vector<pii>, greater<pii> > q;
for(int i = 1 ; i <= 52 ; ++i) d[i] = INF;
d[S] = P;
q.push(pii(d[S], S));
while(!q.empty()){
pii p = q.top(); q.pop();
int u = p.second;
if(d[u] != p.first) continue;
for(int i = 0 ; i < G[u].size() ; ++i){
int v = G[u][i];
if(u <= 26){ // town
ll val = get(d[u]);
g[v][u] = val;
if(d[v] > d[u] + val){
d[v] = d[u] + val;
q.push(pii(d[v], v));
}
} else{ // village
g[v][u] = 1ll;
if(d[v] > d[u] + 1ll){
d[v] = d[u] + 1ll;
q.push(pii(d[v], v));
}
}
}
}
}
vector<char> path;
ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }
int main(){
char uu[10], vv[10];
char s[10], t[10];
int kase = 0;
while(scanf("%d", &n) == 1 && n != -1){
memset(g, 0, sizeof(g));
path.clear();
for(int i = 1 ; i <= 52 ; ++i) G[i].clear();
for(int i = 1 ; i <= n ; ++i){
scanf("%s%s", uu, vv);
G[ID(uu[0])].push_back(ID(vv[0]));
G[ID(vv[0])].push_back(ID(uu[0]));
}
scanf("%lld%s%s", &P, s, t);
dij(ID(t[0]));
printf("Case %d:
", ++kase);
printf("%lld
", d[ID(s[0])]);
int cur = ID(s[0]);
while(1){
path.push_back((cur <= 26) ? cur + 'A' - 1 : cur - 26 + 'a' - 1);
if(cur == ID(t[0])) break;
int tmp = 101;
for(int i = 0 ; i < G[cur].size() ; ++i){
int v = G[cur][i];
if(d[cur] == d[v] + g[cur][v]){
tmp = min(tmp, v);
}
}
cur = tmp;
}
int tt = 0;
for(auto u : path){
if(tt) printf("%c", '-');
tt = 1;
printf("%c", u);
} printf("
");
}
return 0;
}