zoukankan      html  css  js  c++  java
  • SPOJ 371 Boxes

    SPOJ_371 BOXES

        这个题目和HDU_2282几乎是一样的,我们可以把每个“多余”的ball当做一个研究对象,那么它一共有若干种选择,即移动到若干个空位,这样将“多余”的ball看成一组,所有的空位看成另一组,就构成了二分图,于是可以用二分图最优匹配来做。

        但是这样做是O(N^3)的复杂度,即便改成费用流算法,如果建图不加变化的话,依旧是O(N^3)的算法。于是我们要简化思路,尽管对一个空位来讲,可能是任意一个位置的球经过了若干步移到这里,但实际上也就只有两种状况,要么是先移动到这个空位左边的位置,再移动到中间这里,要么就是先移动到这个空位右边的位置,再移动到中间来。因此我们只需要建立相邻两个位置之间的边即可,而不用想上面说的那样将每个“多余”的ball和所有空位都连一条边,这样边的数量就由O(N^2)降低到了O(N),因此总复杂度也就降低到了O(N^2)。

     

    View Code // 二分图最优匹配KM算法 O(N^3)
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<algorithm>
    #define MAXD 1010
    #define MAXM 1000010
    #define INF 0x3f3f3f3f
    int N, M, a[MAXD], id[MAXD], first[MAXD], e, next[MAXM], v[MAXM], w[MAXM];
    int X, Y, A[MAXD], B[MAXD], slack, visx[MAXD], visy[MAXD], yM[MAXD], wM[MAXD];
    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, j, k;
        scanf("%d", &N);
        X = Y = 0;
        for(i = 0; i < N; i ++)
        {
            scanf("%d", &a[i]);
            if(a[i] == 0) id[i] = Y ++;
        }
        memset(first, -1, sizeof(first[0]) * N), e = 0;
        for(i = 0; i < N; i ++)
            if(a[i] > 1)
            {
                for(j = 1; j < a[i]; j ++)
                {
                    for(k = 0; k < N; k ++)
                        if(a[k] == 0)
                            add(X, id[k], N - std::min(abs(k - i), N - abs(k - i)));
                    ++ X;
                }
            }
    }
    int searchpath(int cur)
    {
        int i;
        visx[cur] = 1;
        for(i = first[cur]; i != -1; i = next[i])
            if(!visy[v[i]])
            {
                int temp = A[cur] + B[v[i]] - w[i];
                if(temp == 0)
                {
                    visy[v[i]] = 1;
                    if(yM[v[i]] == -1 || searchpath(yM[v[i]]))
                    {
                        yM[v[i]] = cur, wM[v[i]] = w[i];
                        return 1;
                    }
                }
                else
                    slack = std::min(slack, temp);
            }
        return 0;
    }
    void solve()
    {
        int i, j, ans = 0;
        for(i = 0; i < X; i ++)
        {
            A[i] = 0;
            for(j = first[i]; j != -1; j = next[j]) A[i] = std::max(A[i], w[j]);
        }
        memset(B, 0, sizeof(B[0]) * Y);
        memset(yM, -1, sizeof(yM[0]) * Y);
        for(i = 0; i < X; i ++)
            for(;;)
            {
                slack = INF;
                memset(visx, 0, sizeof(visx[0]) * X);
                memset(visy, 0, sizeof(visy[0]) * Y);
                if(searchpath(i))
                    break;
                for(j = 0; j < X; j ++) if(visx[j]) A[j] -= slack;
                for(j = 0; j < Y; j ++) if(visy[j]) B[j] += slack;
            }
        for(i = 0; i < Y; i ++)
            if(yM[i] != -1)
                ans += N - wM[i];
        printf("%d\n", ans);
    }
    int main()
    {
        int t;
        scanf("%d", &t);
        while(t --)
        {
            init();
            solve();
        }
        return 0;
    }
    View Code // 最小费用最大流算法 O(N^2)
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<algorithm>
    #define MAXD 1010
    #define MAXM 8010
    #define INF 0x3f3f3f3f
    int N, first[MAXD], e, next[MAXM], v[MAXM], flow[MAXM], cost[MAXM];
    int S, T, dis[MAXD], q[MAXD], inq[MAXD], pre[MAXD];
    const int Q = 1005;
    void add(int x, int y, int f, int c)
    {
        v[e] = y, flow[e] = f, cost[e] = c;
        next[e] = first[x], first[x] = e ++;
    }
    void init()
    {
        int i, n;
        scanf("%d", &N);
        S = N, T = N + 1;
        memset(first, -1, sizeof(first[0]) * (T + 1)), e = 0;
        scanf("%d", &n);
        add(S, 0, n, 0), add(0, S, 0, 0), add(0, T, 1, 0), add(T, 0, 0, 0);
        add(0, N - 1, INF, 1), add(N - 1, 0, 0, -1), add(N - 1, 0, INF, 1), add(0, N - 1, 0, -1);
        for(i = 1; i < N; i ++)
        {
            scanf("%d", &n);
            add(S, i, n, 0), add(i, S, 0, n), add(i, T, 1, 0), add(T, i, 0, 0);
            add(i - 1, i, INF, 1), add(i, i - 1, 0, -1), add(i, i - 1, INF, 1), add(i - 1, i, 0, -1);
        }
    }
    int bfs()
    {
        int i, x, front = 0, rear = 0;
        memset(dis, 0x3f, sizeof(dis[0]) * (T + 1));
        dis[S] = 0, pre[S] = -1, q[rear ++] = S;
        memset(inq, 0, sizeof(inq[0]) * (T + 1));
        while(front != rear)
        {
            x = q[front ++], inq[x] = 0;
            front > Q ? front = 0 : 0;
            for(i = first[x]; i != -1; i = next[i])
                if(flow[i] && dis[v[i]] > dis[x] + cost[i])
                {
                    dis[v[i]] = dis[x] + cost[i], pre[v[i]] = i;
                    if(!inq[v[i]])
                    {
                        q[rear ++] = v[i], inq[v[i]] = 1;
                        rear > Q ? rear = 0 : 0;
                    }
                }
        }
        return dis[T] != INF;
    }
    void solve()
    {
        int i, a, c = 0;
        while(bfs())
        {
            for(i = pre[T], a = INF; i != -1; i = pre[v[i ^ 1]]) a = std::min(a, flow[i]);
            for(i = pre[T]; i != -1; i = pre[v[i ^ 1]])
                flow[i] -= a, flow[i ^ 1] += a;
            c += a * dis[T];
        }
        printf("%d\n", c);
    }
    int main()
    {
        int t;
        scanf("%d", &t);
        while(t --)
        {
            init();
            solve();
        }
        return 0;
    }
  • 相关阅读:
    Java以指定格式输入数字
    毕向东JAVA视频讲解(第六课)
    毕向东JAVA视频讲解(四五课)
    毕向东JAVA视频讲解笔记(前三课)
    C++ Primer笔记整理
    map的详细用法
    opencv中的矩阵操作
    Matlab程序 转C++/Opencv基于Mat 不可不知的17个函数
    目标检测的图像特征提取之(三)Haar特征
    目标检测的图像特征提取之(二)LBP特征
  • 原文地址:https://www.cnblogs.com/staginner/p/2640980.html
Copyright © 2011-2022 走看看