题解:
$k<=20,$ 考虑状压dp.
从 $1$ 号点走到 $n$ 号点经过的点的个数可能会非常多,但是强制要求经过的点一共才 $20$ 个.
而我们发现这个题好就好在可以经过某个城市,而不停留.
故我们在关键点之间进行转移的时候可以直接走最短路,而不用管别的.
令方程 $f[i][j]$ 表示访问过的关键点集合为 $i,$ 当前在点 $i$ 的最短路径.
考虑转移:
枚举下一个可以到达的关键节点 $k,$ 能转移到 $k$ 的条件是 $i$ 中有 $k$ 需要提前访问的元素,这个可以提前预处理.
如果成功转移,则方程为 $f[i|(1<<k)][i]+d[j][k],$ 其中 $d[j][k]$ 表示 $j$ 到 $k$ 的最短路.
由于 $j$ 只可能取到 $[1,20],$ 所以只需暴力跑 $20$ 遍最短路即可.
我不会说我还没调过样例~
以后有时间的话再调吧~
Code:
#include <bits/stdc++.h> #define N 20003 #define ll long long #define inf 1000000000 #define M (1<<21) #define setIO(s) freopen(s".in","r",stdin) using namespace std; ll d[30][N],f[M][22]; int n,m,K,edges; int done[N],hd[N],to[N<<1],nex[N<<1],val[N<<1],Log[22],phase[22]; int count(int c) { int l=0; for(;c;c>>=1) if(c&1) ++l; return l; } void getmin(ll &a,ll b) { if(b<a) a=b; } void addedge(int u,int v,int c) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=c; } struct Node { int u; ll dis; Node(int u=0,ll dis=0):u(u),dis(dis){} bool operator<(Node b)const { return b.dis<dis; } }; priority_queue<Node>q; void Dijkstra(int s) { int i,v,u; for(i=0;i<=n;++i) d[s][i]=inf,done[i]=0; for(d[s][s]=0,q.push(Node(s,0));!q.empty();) { Node e=q.top(); q.pop(); u=e.u; if(done[u]) continue; done[u]=1; for(i=hd[u];i;i=nex[i]) { v=to[i]; if(d[s][v]>d[s][u]+val[i]) { d[s][v]=d[s][u]+val[i]; q.push(Node(v,d[s][v])); } } } } int main() { int i,j; setIO("input"); scanf("%d%d%d",&n,&m,&K); for(i=0;i<=20;++i) Log[i]=(1<<i); for(i=1;i<=m;++i) { int a,b,c; scanf("%d%d%d",&a,&b,&c),addedge(a,b,c),addedge(b,a,c); } for(i=1;i<=K+1;++i) Dijkstra(i); if(!K) { printf("%lld ",d[1][n]); return 0; } int Q; scanf("%d",&Q); for(i=1;i<=Q;++i) { int a,b; scanf("%d%d",&a,&b); if(a!=1) phase[b]+=Log[a-2]; } memset(f,0x3f,sizeof(f)); for(int sta=1;sta<Log[K];++sta) { if(count(sta)==1) { for(j=2;j<=K+1;++j) { if(Log[j-2]&sta) { f[sta][j-2]=d[1][j]; printf("%d %lld ",j,f[sta][j-2]); break; } } } for(j=2;j<=K+1;++j) { if(Log[j-2]&sta) { for(int cc=2;cc<=K+1;++cc) { if( !(Log[cc-2]&sta) && ((sta & phase[cc]) == phase[cc]) ) getmin(f[sta|Log[cc-2]][cc-2],f[sta][j-2]+d[j][cc]); } } } } ll ans=10000000000; for(i=2;i<=K+1;++i) { getmin(ans, f[Log[K]-1][i-2]+d[i][n]); } printf("%lld ",ans); return 0; }