zoukankan      html  css  js  c++  java
  • [POI2007]Tourist Attractions

    题目大意:
      给你一个$n(nleq 2 imes 10^4)$个点,$m(mleq 2 imes 10^5)$条边的带边权的连通图。其中有$k(kleq 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{egin{aligned}dis[1][i]&&{pre[i]=varnothing}\infty&&pre[i] eqvarnothingend{aligned} ight.$。
      转移方程为$f[Sigcup i][i]=min{f[S][j]+dis[i][j]mid i otin Sland pre[i]in S}$。
      答案$ans=min{f[U][i]+dis[i][n]}$。
      Floyd$O(k^3)$,配对堆优化Dijkstra$O(m+nlog n)$,动态规划$O(2^kk^2)$时间复杂度为$O(k^3+m+nlog 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(ninom{k}{lceilfrac{k}{2} ceil})$。

      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
    ",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
    ",ans);
    107     return 0;
    108 }
  • 相关阅读:
    菜鸟系列k8s——快速部署k8s集群(3)
    菜鸟系列docker——docker容器(7)
    菜鸟系列Fabric源码学习 — 区块同步
    菜鸟系列Fabric源码学习 — peer节点启动
    菜鸟系列Fabric源码学习 — orderer服务启动
    递归科赫雪花
    汉诺塔递归函数hanoi
    字符串格式化与.format()
    turtle风轮绘制
    接口签名2
  • 原文地址:https://www.cnblogs.com/skylee03/p/8412175.html
Copyright © 2011-2022 走看看