zoukankan      html  css  js  c++  java
  • HDU 3311 Dig The Wells

    HDU_3311

        我表示完全没看懂题意……膜拜看懂题意的各位大神,附一份“偷”来的题意:

        【题目大意】

        给定N个寺庙,和M个另外的地方。

        然后给定点权,表示在这个点挖水井需要的代价。

        再给定边权,为建造无向边i,j的代价。

        然后求怎样弄最小的代价使得前N个点,就是寺庙都能得到水。

        这个和一般的斯坦纳树的题目不同的地方在于挖井要加点权,但是仔细分析一下不难发现,如果1-N作为斯坦纳树的叶子节点一共有两种状态,要么挖井要么不挖井,而其他点做为叶子节点只有一种状态,就是必须挖井,否则由于这个节点就可以被删去就一定不会是最优的方案。在dp之前如果将这些叶子节点的状态都初始化好的话,后面的内容就和普通的斯坦纳树的题目就没有什么区别了,只需要考虑边权即可,另外最后合并连通块dp的时候要判断一下当前集合内是否有水,而且要用两个有水的点集合成当前这个点集。

        由于要判一个连通块内有没有水,所以要多开一个二进制位表示有没有水。

        另外推荐一个感觉讲斯坦纳树讲得不错的博客:http://endlesscount.blog.163.com/blog/static/821197872012525113427573/

    #include<stdio.h>
    #include<string.h>
    #define MAXD 1010
    #define MAXM 10010
    #define ST 74
    #define MAXQ 74010
    #define INF 0x3f3f3f3f
    const int Q = 74000;
    int N, M, P, first[MAXD], e, next[MAXM], v[MAXM], w[MAXM];
    int bit[MAXD], q[MAXQ], front, rear, inq[MAXD][ST];
    int f[MAXD][ST], dp[ST];
    void add(int x, int y, int z)
    {
        v[e] = y, w[e] = z;
        next[e] = first[x], first[x] = e ++;
    }
    void init()
    {
        int i, x, y, z;
        memset(f, 0x3f, sizeof(f));
        memset(bit, 0, sizeof(bit));
        for(i = 1; i <= N + M; i ++)
        {
            if(i <= N) bit[i] = 1 << i - 1;
            scanf("%d", &z);
            if(i <= N) f[i][bit[i]] = 0, f[i][bit[i] | 1 << N] = z;
            else f[i][1 << N] = z;
        }
        memset(first, -1, sizeof(first));
        e = 0;
        for(i = 0; i < P; i ++)
        {
            scanf("%d%d%d", &x, &y, &z);
            add(x, y, z), add(y, x, z);
        }
    }
    int Min(int x, int y)
    {
        return x < y ? x : y;
    }
    void spfa()
    {
        int i, x, st, y, nst;
        while(front != rear)
        {
            x = q[front] & 1023, st = q[front] >> 10;
            inq[x][st] = 0;
            ++ front > Q ? front = 0 : 0;
            for(i = first[x]; i != -1; i = next[i])
            {
                y = v[i], nst = st | bit[y];
                if(f[x][st] + w[i] < f[y][nst])
                {
                    f[y][nst] = f[x][st] + w[i];
                    if(st == nst && !inq[y][nst])
                    {
                        q[rear ++] = nst << 10 | y, inq[y][nst] = 1;
                        rear > Q ? rear = 0 : 0;
                    }
                }
            }
        }
    }
    void solve()
    {
        int i, j, k, nn = 1 << N + 1;
        front = rear = 0;
        memset(inq, 0, sizeof(inq));
        for(i = 0; i < nn; i ++)
        {
            for(j = 1; j <= N + M; j ++)
            {
                for(k = i - 1 & i; k; k = k - 1 & i)
                    f[j][i] = Min(f[j][i], f[j][k | bit[j]] + f[j][i - k | bit[j]]);
                if(f[j][i] < INF)
                {
                    q[rear ++] = i << 10 | j, inq[j][i] = 1;
                    rear > Q ? rear = 0 : 0;
                }
            }
            spfa();
        }
    
        memset(dp, 0x3f, sizeof(dp));
        for(i = 0; i < nn; i ++)
            for(j = 1; j <= N + M; j ++)
                dp[i] = Min(dp[i], f[j][i]);
        for(i = 0; i < nn; i ++)
            if(i & 1 << N)
            {
                for(j = i - 1 & i; j; j = j - 1 & i)
                    if(j & 1 << N)
                        dp[i] = Min(dp[i], dp[j] + dp[i - j | 1 << N]);
            }
        printf("%d\n", dp[(1 << N + 1) - 1]);
    }
    int main()
    {
        while(scanf("%d%d%d", &N, &M, &P) == 3)
        {
            init();
            solve();
        }
        return 0;
    }
  • 相关阅读:
    HDU 2089 不要62
    HDU 5038 Grade(分级)
    FZU 2105 Digits Count(位数计算)
    FZU 2218 Simple String Problem(简单字符串问题)
    FZU 2221 RunningMan(跑男)
    FZU 2216 The Longest Straight(最长直道)
    FZU 2212 Super Mobile Charger(超级充电宝)
    FZU 2219 StarCraft(星际争霸)
    FZU 2213 Common Tangents(公切线)
    FZU 2215 Simple Polynomial Problem(简单多项式问题)
  • 原文地址:https://www.cnblogs.com/staginner/p/2620945.html
Copyright © 2011-2022 走看看