[POI2007]Tourist Attractions
题目大意:
给你一个$n(n\leq 2\times 10^4)$个点,$m(m\leq 2\times 10^5)$条边的带边权的连通图。其中有$k(k\leq 20)$个关键点。关键点之间有$g$条拓扑结构的依赖关系,每条依赖关系$(u,v)$描述点$v$依赖于点$u$,即点$u$必须在点$v$之前出现。若同时存在依赖关系$(u,v)$和$(v,w)$,则有依赖关系$(u,w)$。每个点可以经过多次,经过的可以不满足依赖关系。求一条从$1$到$n$的最短的路径,满足每个关键点至少有一次被经过时满足了依赖关系。
思路:
状压DP。
首先用Floyd预处理每个关键点依赖的点集$pre[i]$。然后用Dijkstra求出点$1$和每个关键点作为起点的单源最短路。
用$f[S][i]$表示已满足依赖关系的点集为$S$,当前路径上,最后一个结点为$i$。
预处理$f[i][i]=\left\{\begin{aligned}dis[1][i]&&{pre[i]=\varnothing}\\\infty&&pre[i]\neq\varnothing\end{aligned}\right.$。
转移方程为$f[S\bigcup i][i]=\min\{f[S][j]+dis[i][j]\mid i\notin S\land pre[i]\in S\}$。
答案$ans=\min{f[U][i]+dis[i][n]}$。
Floyd$O(k^3)$,配对堆优化Dijkstra$O(m+n\log n)$,动态规划$O(2^kk^2)$时间复杂度为$O(k^3+m+n\log n+2^kk^2)$。空间复杂度$O(2^kk)$。
在BZOJ上跑了9068 MS,内存89980 KB,Rank 2。但是POI原题内存是64 MB。
一种卡内存的方法是压缩一下状态,因为$f[S][i]$中$S$一定包括$i$,因此我们可以把$i$这一位去掉,然后把大于$i$的位往前移。空间除以一个常数,可以卡过。
还有一种做法是根据$S$中元素个数,将DP数组进行滚动,空间复杂度为$O(n\binom{k}{\lceil\frac{k}{2}\rceil})$。
1 #include<cstdio> 2 #include<cctype> 3 #include<vector> 4 #include<climits> 5 #include<functional> 6 #include<ext/pb_ds/priority_queue.hpp> 7 inline int getint() { 8 register char ch; 9 while(!isdigit(ch=getchar())); 10 register int x=ch^'0'; 11 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 12 return x; 13 } 14 const int N=20001,K=20; 15 const int inf=INT_MAX; 16 struct Edge { 17 int to,w; 18 }; 19 std::vector<Edge> e[N]; 20 inline void add_edge(const int &u,const int &v,const int &w) { 21 e[u].push_back((Edge){v,w}); 22 e[v].push_back((Edge){u,w}); 23 } 24 bool b[K][K]; 25 int n,m,k,pre[K],dis0[N],dis[K][N],f[1<<K][K]; 26 struct Vertex { 27 int id,dis; 28 bool operator > (const Vertex &another) const { 29 return dis>another.dis; 30 } 31 }; 32 void dijkstra(const int &s,int dis[]) { 33 static __gnu_pbds::priority_queue<Vertex,std::greater<Vertex> > q; 34 static __gnu_pbds::priority_queue<Vertex,std::greater<Vertex> >::point_iterator p[N]; 35 for(register int i=1;i<=n;i++) { 36 p[i]=q.push((Vertex){i,dis[i]=i==s?0:inf}); 37 } 38 while(!q.empty()) { 39 const int x=q.top().id; 40 q.pop(); 41 for(register unsigned i=0;i<e[x].size();i++) { 42 const int &y=e[x][i].to,&w=e[x][i].w; 43 if(dis[x]+w<dis[y]) { 44 q.modify(p[y],(Vertex){y,dis[y]=dis[x]+w}); 45 } 46 } 47 } 48 } 49 int main() { 50 n=getint(),m=getint(),k=getint(); 51 for(register int i=0;i<m;i++) { 52 const int u=getint(),v=getint(),w=getint(); 53 add_edge(u,v,w); 54 } 55 if(k==0) { 56 dijkstra(1,dis0); 57 printf("%d\n",dis0[n]); 58 return 0; 59 } 60 for(register int i=getint();i;i--) { 61 const int u=getint(),v=getint(); 62 b[u-2][v-2]=true; 63 } 64 for(register int l=0;l<k;l++) { 65 for(register int i=0;i<k;i++) { 66 if(i==l||!b[i][l]) continue; 67 for(register int j=0;j<k;j++) { 68 if(j==l||j==i||!b[l][j]) continue; 69 b[i][j]=true; 70 } 71 } 72 } 73 for(register int i=0;i<k;i++) { 74 for(register int j=0;j<k;j++) { 75 if(b[i][j]) pre[j]|=1<<i; 76 } 77 } 78 dijkstra(1,dis0); 79 for(register int i=2;i<=k+1;i++) { 80 dijkstra(i,dis[i-2]); 81 } 82 for(register int state=1;state<1<<k;state++) { 83 for(register int i=0;i<k;i++) { 84 f[state][i]=inf; 85 } 86 } 87 for(register int i=0;i<k;i++) { 88 if(!pre[i]) f[1<<i][i]=dis0[i+2]; 89 } 90 for(register int state=1;state<1<<k;state++) { 91 for(register int i=0;i<k;i++) { 92 if(!(state&(1<<i))&&(state&pre[i])==pre[i]) { 93 for(register int j=0;j<k;j++) { 94 if(f[state][j]!=inf) { 95 f[state^(1<<i)][i]=std::min(f[state^(1<<i)][i],f[state][j]+dis[j][i+2]); 96 } 97 } 98 } 99 } 100 } 101 int ans=inf; 102 for(register int i=0;i<k;i++) { 103 if(f[(1<<k)-1][i]==inf) continue; 104 ans=std::min(ans,f[(1<<k)-1][i]+dis[i][n]); 105 } 106 printf("%d\n",ans); 107 return 0; 108 }