• BZOJ 1097: [POI2007]旅游景点atr 状态压缩+Dijkstra


    题解: 

    $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;    
    }
    

      

  • 相关阅读:
    操作系统原理
    Linux三剑客正则表达式
    Linux通配符知识深度实践详解
    Linux文件属性之时间戳及文件名知识详解
    Linux系统文件权限
    date:显示与设置系统时间
    正则表达式--三剑客简单应用
    Linux习题小结
    Linux系统文件属性知识
    Linux系统目录结构知识
  • 原文地址:https://www.cnblogs.com/guangheli/p/11544567.html
Copyright © 2020-2023  润新知