题目传送门:https://uva.onlinejudge.org/index.php?problem=3468
中文题面:《算法竞赛·入门经典·训练指南》P331
一直以为是一个建图非常神奇的最短路/网络流
然而居然是类似斯坦纳树一样的转移怪异的DP

设d[i]为满足题目条件的情况下,到达第i个点时的最少货物量
然后反向各更新即可
更新的方式的话,显然SPFA的正确性没有问题
又因为没有负权,所以Dijkstra也是正确的?
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 70;
const int M = 2000+9;
const double EPS = 1e-4;
int head[N],nxt[M],to[M],m,T,MN,s,t,case_cnt;
LL d[N]; bool inq[N]; queue<int> que;
inline int read(){
char c=getchar(); int ret=0,f=1;
while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
return ret*f;
}
inline int ID(const char c) {
if ('a' <= c && c <= 'z') return c - 'a' + 1;
else return c - 'A' + 27;
}
inline void Add_Edge(int u, int v) {
to[++T] = v; nxt[T] = head[u]; head[u] = T;
to[++T] = u; nxt[T] = head[v]; head[v] = T;
}
inline LL cost(int w, LL val) {
if (w == s) return 0;
else if (w <= 26) return 1;
else {
LL l=1,r=1e15,ret=1,mid;
while (l <= r) {
mid = l + r >> 1;
if (val+mid-(LL)(ceil((val+mid)/20.0)+EPS) >= val) ret = mid, r = mid - 1;
else l = mid+1;
}
return ret;
}
}
inline LL SPFA() {
memset(d,0x3f,sizeof(d));
d[t] = MN + (s!=t?cost(t, MN):0);
inq[t] = 1; que.push(t);
while (!que.empty()) {
int w = que.front(); inq[w] = 0; que.pop();
for (int i=head[w];i;i=nxt[i]) {
if (d[to[i]] > d[w] + cost(to[i],d[w])) {
d[to[i]] = d[w] + cost(to[i],d[w]);
if (!inq[to[i]]) inq[to[i]] = 1, que.push(to[i]);
}
}
}
return d[s];
}
int main(){
static char U[10],V[10];
for (m=read();~m;m=read(),T=0) {
memset(head,0,sizeof(head));
for (int i=1;i<=m;i++) {
scanf("%s %s",U+1,V+1);
Add_Edge(ID(U[1]), ID(V[1]));
}
MN = read();
scanf("%s %s",U+1,V+1);
s = ID(U[1]); t = ID(V[1]);
printf("Case %d: %lld\n",++case_cnt,SPFA());
}
return 0;
}