zoukankan      html  css  js  c++  java
  • [牛客]十二桥问题 解题报告

    [牛客]十二桥问题

    题意

    有一张 (n) 个点 (m) 条边的带权有向图 ((n le 50000, m le 200000)),
    其中有 (k) 条边是一定要经过的 ((k le 12)),

    从节点 (1) 出发, 求 : 经过了所有必须经过的边之后,又回到节点 (1) 的最短路径.

    思路

    首先, 虽然点的数量比较大, 但是大部分是没用的, 我们只需要关注必须经过的那几条边 (称为桥) 的端点.

    所以, 一开始先分别以 (1) 节点和所有桥的端点作为起点, 跑若干遍 (dijkstra), 并用这若干个点之间的最短距离建一张新图.

    然后, 考虑状压dp, 设 (f[u][i]) 为到了当前点为 (u), 经过的边集(桥集)为 (i) 时的最短路径.

    转移时, 对于当前边集中的边的端点, 枚举它们上一次经过了哪条边转移,
    由于题目保证整张图联通, 所以新的最短路图是一个完全图, 因此不是当前边集中的边的端点的点的状态是无用的, 不需要转移.

    代码

    #include<bits/stdc++.h>
    #define ll long long
    #define mkp make_pair
    using namespace std;
    const int N=5e4+7;
    const int M=2e5+7;
    const int K=12+7;
    const int L=4096+7;
    int n,m,k,all,poi,num[N],cnt,bdg[2*K][2*K],bi[K],rec[L];
    ll f[2*K+1][L],dis[N],mp[2*K+1][2*K+1],ans,inf;
    int lst[N],nxt[2*M],to[2*M],tot;
    ll len[2*M];
    bool b[N];
    struct bridge{
        int x,y;
        ll w;
    }e[M];
    void add(int x,int y,ll w){
        nxt[++tot]=lst[x];
        to[tot]=y;
        len[tot]=w;
        lst[x]=tot;
    }
    void read(){
        cin>>n>>m>>k; num[1]=++cnt; int x,y;
        for(int i=1;i<=m;i++){
            scanf("%d%d%lld",&x,&y,&e[i].w);
            if(!num[x]) num[x]=++cnt;
            if(!num[y]) num[y]=++cnt;
            x=num[x];
            y=num[y];
            e[i].x=x;
            e[i].y=y;
            add(x,y,e[i].w);
            add(y,x,e[i].w);
            if(i<=k){ bdg[x][y]=bdg[y][x]=i; poi=cnt; }
        }
        for(int i=1;i<=k;i++){ bi[i]=1<<i-1; rec[bi[i]]=i; }
        all=(1<<k)-1;
        memset(f,127,sizeof(f));
        memset(mp,-1,sizeof(mp));
        inf=f[0][0];
    }
    priority_queue<pair<ll,int> >h;
    void dijk(int st){
        memset(dis,127,sizeof(dis));
        memset(b,0,sizeof(b));
        dis[st]=0;
        h.push(mkp(0,st));
        while(!h.empty()){
            int u=h.top().second; h.pop();
            if(b[u]) continue;
            b[u]=1;
            for(int i=lst[u];i;i=nxt[i])
                if(dis[to[i]]>dis[u]+len[i]){
                    dis[to[i]]=dis[u]+len[i];
                    h.push(mkp(-dis[to[i]],to[i]));
                }
        }
        for(int i=1;i<=2*k+1;i++)
            if(dis[i]!=inf) mp[st][i]=dis[i];
    }
    void print(int x){
        for(int i=1;i<=k;i++){
            printf("%d",x&1);
            x>>=1;
        }
    }
    int main(){
    //  freopen("bridge.in","r",stdin);
    //  freopen("bridge.out","w",stdout);
        read();
        for(int i=1;i<=2*k+1;i++) dijk(i);
        f[1][0]=0; h.push(mkp(0,1));
        n=poi;
        ans=inf;
        for(int i=0;i<=all;i++){
            for(int j=i;j;j-=j&-j){
                int t=rec[j&-j];
                int x=e[t].x,y=e[t].y;
                for(int v=1;v<=n;v++)
                	if(f[v][i^bi[t]]!=inf){
                		f[x][i]=min(f[x][i],f[v][i^bi[t]]+mp[y][v]+e[t].w);
                		f[y][i]=min(f[y][i],f[v][i^bi[t]]+mp[x][v]+e[t].w);
                	}
    //            printf("%d ",x); print(i); printf(": %lld
    ",f[x][i]);
    //            printf("%d ",y); print(i); printf(": %lld
    ",f[y][i]);
            }
        }
        for(int i=1;i<=n;i++)
            if(f[i][all]!=inf) ans=min(ans,f[i][all]+mp[i][1]);
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    XE8下安装IntraWeb 14.0.40和D7下安装IntraWeb 11.0.63破解版的正确方法
    网易博客打不开怎么办
    SQL SERVER 导入EXCEL的存储过程
    TMemoryStream、String与OleVariant互转
    【转载】Delphi Idhttp的get和post方法
    sqlserver得到行号
    Delphi 中的 XMLDocument 类详解(5)
    10款免费且开源的项目管理工具
    iOS开发者必备:九大设计类工具
    15个步骤创立技术公司,并收获千万用户(完结)
  • 原文地址:https://www.cnblogs.com/BruceW/p/11837513.html
Copyright © 2011-2022 走看看