• bzoj 4774: 修路


    Time Limit: 10 Sec  Memory Limit: 256 MB
    Submit: 282  Solved: 132
    [Submit][Status][Discuss]

    Description

    村子间的小路年久失修,为了保障村子之间的往来,法珞决定带领大家修路。对于边带权的无向图 G = (V, E),
    请选择一些边,使得1 <= i <= d, i号节点和 n - i + 1 号节点可以通过选中的边连通,最小化选中的所有边
    的权值和。
     

    Input

    第一行两个整数 n, m,表示图的点数和边数。接下来的 m行,每行三个整数 ui, vi, wi,表示有一条 ui 与 vi 
    之间,权值为 wi 的无向边。
    1 <= d <= 4
    2d <= n <= 10^4
    0 <= m <= 10^4
    1 <= ui, vi <= n
    1 <= wi <= 1000

    Output

    一行一个整数,表示答案,如果无解输出-1

    Sample Input

    10 20 1
    6 5 1
    6 9 4
    9 4 2
    9 4 10
    6 1 2
    2 3 6
    7 6 10
    5 7 1
    9 7 2
    5 9 10
    1 6 8
    4 7 4
    5 7 1
    2 6 9
    10 10 6
    8 7 2
    10 9 10
    1 2 4
    10 1 8
    9 9 7

    Sample Output

    8
     
    好像是个斯坦纳树的模板题。。。
    虽然到现在也不知道斯坦纳树具体是个什么玩意,,,大概就是能处理有后效性的图上状压dp ???
    因为关键点很少,所以可以状压dp。
    设f(S,i)为目前选的边的构成的树的根是i,且关键点集合状态是S的最优值。
    转移有两种:
    1. f(S,i)=min{f(s,i)+f(S^s,i)},其中s是S的子集。
    相当于合并两颗树(如果有重复边也不要紧,因为有重复边的话最后一定不会在最优答案中)
    2.f(S,i)=min{f(S,x)+val(x,i)},其中x与i有边相连。
    相当于将树根扩展出去一个节点。
     
    最后再根据f数组进行子集dp,只能从满足条件的子集转移(即有i号节点必须有n-i+1号节点)
     
    /**************************************************************
        Problem: 4774
        User: JYYHH
        Language: C++
        Result: Accepted
        Time:2180 ms
        Memory:14904 kb
    ****************************************************************/
     
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    using namespace std;
    int f[300][10005],n,m,hd[10005],d;
    int to[20005],val[20005],ne[20005];
    int dis[10005],ans[300],num,ci[20],all;
    bool iq[10005];
    int q[400005],l,r;
    int main(){
        memset(f,0x3f,sizeof(f));
        memset(ans,0x3f,sizeof(ans));
        ci[0]=1;
        for(int i=1;i<=10;i++) ci[i]=ci[i-1]<<1;
         
        scanf("%d%d%d",&n,&m,&d);
        int uu,vv,ww;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&uu,&vv,&ww);
            to[++num]=vv,ne[num]=hd[uu],hd[uu]=num,val[num]=ww;
            to[++num]=uu,ne[num]=hd[vv],hd[vv]=num,val[num]=ww;     
        }
         
        for(int i=1;i<=d;i++) f[ci[i-1]][i]=0;
        for(int i=n-d+1;i<=n;i++) f[ci[n+d-i]][i]=0;
        memset(f[0],0,sizeof(f[0]));
         
        all=ci[d<<1]-1;
        for(int S=1;S<=all;S++){
            for(int i=1;i<=n;i++)
                for(int s=(S-1)&S;s;s=(s-1)&S) f[S][i]=min(f[S][i],f[s][i]+f[s^S][i]);
             
            for(int i=1;i<=n;i++) dis[i]=f[S][i],q[i]=i,iq[i]=1;
             
            l=1,r=n;
            while(l<=r){
                int x=q[l++];
                for(int i=hd[x];i;i=ne[i]) if(dis[x]+val[i]<dis[to[i]]){
                    dis[to[i]]=dis[x]+val[i];
                    if(!iq[to[i]]) q[++r]=to[i],iq[to[i]]=1;
                }
                iq[x]=0;
            }
             
            for(int i=1;i<=n;i++) f[S][i]=dis[i];
        }
         
        for(int s=0;s<=all;s++){
            bool fl=0;
            for(int i=1;i<=d;i++) if(((s&ci[i-1])?1:0)+((s&ci[i+d-1])?1:0)==1){
                fl=1;
                break;
            }
            if(fl) continue;
             
            for(int i=1;i<=n;i++) ans[s]=min(ans[s],f[s][i]);
        }
         
        for(int S=1;S<=all;S++)
            for(int s=(S-1)&S;s;s=(s-1)&S) ans[S]=min(ans[S],ans[s]+ans[S^s]);
         
        if(ans[all]!=ans[all+1]) printf("%d
    ",ans[all]);
        else puts("-1");
        return 0;
    }
     
     
  • 相关阅读:
    入职外包一个月的感受!(读者投稿)
    作为架构风格的 REST 到底是什么
    PHP基础之查找
    PHP基础之排序
    PHP入门之数组
    PHP入门之函数
    PHP入门之流程控制
    PHP入门之类型与运算符
    DC-1靶机实战和分析
    【基础算法】 状态压缩DP---蒙德里安的梦想
  • 原文地址:https://www.cnblogs.com/JYYHH/p/8302867.html
Copyright © 2020-2023  润新知