题意:从起始点到目的地有村庄(小写字母)和城市(大写字母)经过,到达一个村庄要缴纳一个单位的货物,到达一个城镇每20个单位货物缴纳4个单位货物,问到达目的地剩余p个单位货物时至少需要携带多少货物出发,并打印路径(字典序最小)
思路:从目的地反过来找最短路,当前地方是村庄的话,下一个地方需要加1,否则二分找出这个值
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
typedef long long ll;
const int maxn = 510;
const ll INF = 1e18;
using namespace std;
struct P {
char t;
ll cost;
P(char ch, ll c) : t(ch), cost(c) {}
};
int t = 1, n, p[maxn];
ll dp[maxn], tal;
vector<P> G[maxn];
bool operator < (P a, P b) {
return a.cost > b.cost;
}
void init() {
for(int i = 0; i < maxn; i++)
G[i].clear();
fill(dp, dp + maxn, INF);
memset(p, -1, sizeof(p));
}
ll solve(ll x) {
ll l = x, r = 1e12;
while(l < r) {
ll mid = (l + r) >> 1;
ll d = mid / 20;
if(mid % 20) d++;
ll res = mid - d;
if(res >= x) r = mid;
else l = mid + 1;
}
return r;
}
void dijkstra(char from, char to) {
dp[to] = tal;
priority_queue<P> q;
q.push(P(to, 0));
while(!q.empty()) {
P st = q.top(); q.pop();
char u = st.t;
if(dp[u] < st.cost) continue;
for(int i = 0; i < G[u].size(); i++) {
char Next = G[u][i].t;
ll co;
if(u >= 'a' && u <= 'z') co = dp[u] + 1;
else co = solve(dp[u]);
if(co < dp[Next]) {
dp[Next] = co;
p[Next] = u;
q.push(P(Next, co));
} else if(co == dp[Next]) {
if(u < p[Next]) p[Next] = u;
}
}
}
if(dp[from] >= INF) dp[INF] = -1;
}
void dfs(int x) {
if(x == -1) return ;
printf("%c", x);
if(p[x] > 0) printf("-");
dfs(p[x]);
}
int main() {
char f[5], s[5];
while(scanf("%d", &n) && n != -1) {
init();
for(int i = 0; i < n; i++) {
scanf("%s %s", f, s);
G[f[0]].push_back(P(s[0], 0));
G[s[0]].push_back(P(f[0], 0));
}
scanf("%lld %s %s", &tal, f, s);
dijkstra(f[0], s[0]);
printf("Case %d:\n%lld\n", t++, dp[f[0]]);
dfs(f[0]);
printf("\n");
}
return 0;
}