• Board Game FZU


     Problem 2143 Board Game

    Accept: 128    Submit: 327
    Time Limit: 1000 mSec    Memory Limit : 32768 KB

     Problem Description

    Fat brother and Maze are playing a kind of special (hentai) game on an N*M board (N rows, M columns). At the beginning, each grid of the board which is own by Fat brother is consisting of an integer 0. At each turn, he can choose two adjacent grids and add both the integer inside them by 1. But due to some unknown reason, the number of each grid can not be large than a given integer K. Also, Maze has already drown an N*M board with N*M integers inside each grid. What Fat brother would like to do is adding his board to be as same as Maze’s. Now we define the different value of two boards A and B as:

    Now your task is to help Fat brother the minimal value of S he can get.

     Input

    The first line of the date is an integer T, which is the number of the text cases.

    Then T cases follow, each case contains three integers N, M and K which are mention above. Then N lines with M integers describe the board.

    1 <= T <= 100, 1 <= N, M, K <= 9

    0 <= the integers in the given board <= 9

     Output

    For each case, output the case number first, then output the minimal value of S Fat brother can get.

     Sample Input

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

     Sample Output

    Case 1: 0 Case 2: 2 Case 3: 81 Case 4: 33 Case 5: 5
     
    看到棋盘模型 先来一套黑白染色冷静一下
     
    看准了 是最小费用流 不是最小费用最大流
    是的 这题没有在最大流的基础下求最小费用
    如何控制流量? 因为spfa是每次找一条增广路,并找到这条增广路的最小容量,且更新。 如果把边的容量设置为K,那么每次我们就不能有效的控制流量
    所以拆边,用K条边,每条边的容量为1,当然同时我们还要加上约束条件,不然就和没拆一样
    约束条件设置在边的费用上,这K条边 每条边费用为 流当前边所获得的总的费用 减去 流上一条边所获得的总的费用
    (i - B)2 - (i - 1 - B)2
    那么把流过的边加起来是不是就是 总的费用
    当 流当前边所获得的总的费用 减去 流上一条边所获得的总的费用 >= 0 时 那么就说明 流到当前边获得的总费用 大于等于 流上一条边获得的总费用
    在整个增广路上的体现是 d[t] >= 0 (d[t]即为这条增广路所带来的费用) 这时我们就结束spfa就好啦
     
    还有 第一条边是(1 - B)2 - (0 - B)2 所以当前i条边加起来的时候 就会多减B2 所以在输入每个格子的值的时候 value += tmp * tmp;
     
    总的来说就是拆边 每条边的费用即为停止的判断条件

     
    #include <iostream>
    #include <cstdio>
    #include <sstream>
    #include <cstring>
    #include <map>
    #include <cctype>
    #include <set>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <algorithm>
    #include <list>
    #include <cmath>
    #include <bitset>
    #define rap(i, a, n) for(int i=a; i<=n; i++)
    #define rep(i, a, n) for(int i=a; i<n; i++)
    #define lap(i, a, n) for(int i=n; i>=a; i--)
    #define lep(i, a, n) for(int i=n; i>a; i--)
    #define rd(a) scanf("%d", &a)
    #define rlld(a) scanf("%lld", &a)
    #define rc(a) scanf("%c", &a)
    #define rs(a) scanf("%s", a)
    #define rb(a) scanf("%lf", &a)
    #define rf(a) scanf("%f", &a)
    #define pd(a) printf("%d
    ", a)
    #define plld(a) printf("%lld
    ", a)
    #define pc(a) printf("%c
    ", a)
    #define ps(a) printf("%s
    ", a)
    #define MOD 2018
    #define LL long long
    #define ULL unsigned long long
    #define Pair pair<int, int>
    #define mem(a, b) memset(a, b, sizeof(a))
    #define _  ios_base::sync_with_stdio(0),cin.tie(0)
    //freopen("1.txt", "r", stdin);
    using namespace std;
    const int maxn = 1100, INF = 0x7fffffff;
    int n, m, s, t;
    int head[maxn], nex[maxn << 1], f[maxn], d[maxn], p[maxn], vis[maxn];
    int cnt, value, flow;
    int dis[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
    
    
    struct node
    {
        int u, v, w, c;
    }Node[maxn << 1];
    
    void add_(int u, int v, int w, int c)
    {
        Node[cnt].u = u;
        Node[cnt].v = v;
        Node[cnt].w = w;
        Node[cnt].c = c;
        nex[cnt] = head[u];
        head[u] = cnt++;
    }
    
    void add(int u, int v, int w, int c)
    {
        add_(u, v, w, c);
        add_(v, u, -w, 0);
    }
    
    bool spfa()
    {
        for(int i=  0; i < maxn; i++) d[i] = INF;
        deque<int> Q;
        mem(vis, 0);
        mem(p, -1);
        Q.push_front(s);
        vis[s] = 1;
        p[s] = d[s] = 0;
        f[s] = INF;
        while(!Q.empty())
        {
            int u = Q.front(); Q.pop_front();
            vis[u] = 0;
            for(int i = head[u]; i != -1; i = nex[i])
            {
                int v = Node[i].v;
                if(d[v] > d[u] + Node[i].w && Node[i].c > 0)
                {
                    d[v] = d[u] + Node[i].w;
                    p[v] = i;
                    f[v] = min(Node[i].c, f[u]);
                    if(!vis[v])
                    {
                        if(Q.empty()) Q.push_back(v);
                        else if(d[v] < d[Q.front()]) Q.push_front(v);
                        else Q.push_back(v);
                        vis[v] = 1;
                    }
                }
            }
        }
        if(d[t] >= 0) return 0;
        flow += f[t]; value += d[t] * f[t];
        for(int i = t; i != s; i = Node[p[i]].u)
        {
            Node[p[i]].c -= f[t];
            Node[p[i] ^ 1].c += f[t];
        }
        return 1;
    }
    
    
    
    
    void init()
    {
        mem(head, -1);
        cnt = 0;
        value = flow = 0;
    }
    
    
    void max_flow()
    {
        while(spfa());
        pd(value);
    }
    
    
    
    
    int main()
    {
        int T, K, kase = 0;
        rd(T);
        while(T--)
        {
            init();
            rd(n), rd(m), rd(K);
            s = 0, t = n * m + 1;
            for(int i = 1; i <= n; i++)
            {
                for(int j = 1; j <= m; j++)
                {
                    int tmp;
                    rd(tmp);
                    value += tmp * tmp;
                    for(int k = 1; k <= K; k++)
                        if((i + j) & 1) add(s, (i - 1) * m + j, (k - tmp) * (k - tmp) - (k - 1 - tmp) * (k - 1 - tmp), 1);
                        else add((i - 1) * m + j, t, (k - tmp) * (k - tmp) - (k - 1 - tmp) * (k - 1 - tmp), 1);
                    if(( i + j) & 1)
                        for(int k = 0; k < 4; k++)
                        {
                            int nx = i + dis[k][0];
                            int ny = j + dis[k][1];
                            if(nx < 1 || ny < 1 || nx > n || ny > m) continue;
                            add((i - 1) * m + j, (nx - 1) * m + ny, 0, INF);
                        }
                }
            }
            printf("Case %d: ", ++kase);
            max_flow();
    
        }
    
        return 0;
    }
     
  • 相关阅读:
    字符串中包含最多的字符
    循环左移操作 (左旋转字符串)
    翻转字符串
    和为s的连续正数序列
    Unity之使用技巧记录
    Unity资源
    各种IDE的使用
    Unity3d之MonoBehavior自带方法的执行顺序
    c#之new关键词——隐藏基类方法
    算法——各种类型对象通用的二分法插入排序
  • 原文地址:https://www.cnblogs.com/WTSRUVF/p/10810144.html
Copyright © 2020-2023  润新知