[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 }

 

转载于:https://www.cnblogs.com/skylee03/p/8412175.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值