zoukankan      html  css  js  c++  java
  • 【HDU3311】Dig The Wells-状压DP+SPFA:斯坦纳树

    测试地址:Dig The Wells
    题目大意:n个寺庙和m个其他地点,每个寺庙或者地点都可以挖井,现在要修建一些道路,使得每一个寺庙都至少与一个井连通,挖井和修路都需要费用,求最小费用。
    做法:本题需要用到状压DP+SPFA。
    什么是斯坦纳树?在一个图中,连通一个关键点集并使得所选边权和最小的边集构成一棵树,这就是斯坦纳树。
    斯坦纳树可以这样求:令dp(i,state)为选择i点为树根,和关键点连通的状态为state的最小费用,状态转移方程有两个部分:
    第一个部分是f(i,state)=min(f(i,subset)+f(i,statesubset)),其中subsetstate的一个子集。这一个部分是用以自己为根的连通状态组合来更新的答案。这一部分显然可以用枚举子集的状压DP做到O(m3n)
    第二个部分是f(i,state)=min(f(j,state)+dis(i,j)),其中dis(i,j)为点i和点j的距离。这一个部分是用以其他点为根的相同连通状态来更新答案。我们发现这个方程很像最短路算法中的“松弛”操作,因此我们用SPFA来进行松弛,那么这一个部分的时间复杂度为O(kp2n),其中k是SPFA算法的常数。
    那么这道题基本上就是这样做的,然而还是有不同,这道题可以有很多不同的连通块,所以我们可以先按照上面的方法DP,然后求出fmin(state),表示连通state中的这些点所需要的最小费用,这里的费用包括打井的费用,接着令d(i,state)为选了i个连通块,已选点的集合为state的最小费用,则有:
    d(i,state)=min(d(i1,subset)+fmin(statesubset))
    这个显然可以用枚举子集的状压DPO(n3n)算出,那么最后max(d(i,2n1))就是所求的答案。
    我傻逼的地方:在用1表示无穷大的时候,一定要记得确认将要进行的运算中没有包含这种无穷大的数字……WA了好几发……
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m,p;
    int first[1010],tot,st[50],num[50];
    ll val[1010],dp[1010][50],d[2][50],mn[50];
    bool vis[1010]={0};
    queue<int> Q;
    struct edge
    {
        int v,next;
        ll w;
    }e[10010];
    
    void insert(int a,int b,ll w)
    {
        e[++tot].v=b;
        e[tot].next=first[a];
        e[tot].w=w;
        first[a]=tot;
    }
    
    bool cmp(int a,int b)
    {
        return num[a]<num[b];
    }
    
    void init()
    {
        for(int i=1;i<=n+m;i++)
            scanf("%lld",&val[i]);
    
        memset(first,0,sizeof(first));
        tot=0;
        for(int i=1;i<=p;i++)
        {
            int a,b;
            ll w;
            scanf("%d%d%lld",&a,&b,&w);
            insert(a,b,w),insert(b,a,w);
        }
    
        for(int i=0;i<(1<<n);i++)
            for(int j=1;j<=n+m;j++)
                dp[j][i]=-1;
        for(int i=n+1;i<=n+m;i++)
            dp[i][0]=0;
    
        for(int i=0;i<(1<<n);i++)
            st[i]=i;
        sort(st,st+(1<<n),cmp);
    }
    
    void spfa(int state)
    {
        while(!Q.empty())
        {
            int v=Q.front();Q.pop();
            for(int i=first[v];i;i=e[i].next)
                if (dp[e[i].v][state]==-1||dp[e[i].v][state]>dp[v][state]+e[i].w)
                {
                    dp[e[i].v][state]=dp[v][state]+e[i].w;
                    if (!vis[e[i].v]) vis[e[i].v]=1,Q.push(e[i].v);
                }
            vis[v]=0;
        }
    }
    
    void work()
    {
        for(int i=1;i<(1<<n);i++)
        {
            int x=st[i];
            for(int j=1;j<=n+m;j++)
            {
                dp[j][x]=-1;
                if (j<=n&&x==(1<<(j-1))) dp[j][x]=0;
                for(int k=x;k;k=(k-1)&x)
                    if (dp[j][k]!=-1&&dp[j][x-k]!=-1)
                    {
                        if (dp[j][x]==-1) dp[j][x]=dp[j][k]+dp[j][x-k];
                        dp[j][x]=min(dp[j][x],dp[j][k]+dp[j][x-k]);
                    }
                if (dp[j][x]!=-1) Q.push(j),vis[j]=1;
            }
            spfa(x);
            mn[x]=-1;
            for(int j=1;j<=n+m;j++)
                if (dp[j][x]!=-1)
                {
                    if (mn[x]==-1) mn[x]=dp[j][x]+val[j];
                    mn[x]=min(mn[x],dp[j][x]+val[j]);
                }
        }
    
        int now=1,past=0;
        for(int i=0;i<(1<<n);i++)
            d[past][i]=-1;
        d[past][0]=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<(1<<n);j++)
            {
                d[now][j]=d[past][j];
                for(int k=j;k;k=(k-1)&j)
                    if (d[past][j-k]!=-1&&mn[k]!=-1)
                    {
                        if (d[now][j]==-1) d[now][j]=d[past][j-k]+mn[k];
                        d[now][j]=min(d[now][j],d[past][j-k]+mn[k]);
                    }
            }
            swap(now,past);
        }
        printf("%lld
    ",d[past][(1<<n)-1]);
    }
    
    int main()
    {
        for(int i=0;i<=45;i++)
        {
            num[i]=0;
            int x=i;
            while(x)
            {
                num[i]++;
                x-=(x&(-x));
            }
        }
    
        while(scanf("%d%d%d",&n,&m,&p)!=EOF)
        {
            init();
            work();
        }
    
        return 0;
    }
  • 相关阅读:
    echarts二维坐标这样写出立体柱状图
    echarts中使图表循环显示tooltip-封装tooltip的方法轮询显示图表数据
    webpack--运行npm run dev自动打开浏览器以及热加载
    exports、module.exports和export、export default到底是咋回事,区别在哪里
    H5页面判断客户端是iOS或是Android并跳转对应链接唤起APP
    关于页面锚点跳转以及万能锚点跳转插件
    echarts Map 省份文字居中,即对应地图中心位置
    Moment.js 常见用法,常见API
    Tomcat 不加载图片验证码
    Cglib 动态代理
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793399.html
Copyright © 2011-2022 走看看