• bzoj 2756奇怪的游戏


    2756: [SCOI2012]奇怪的游戏

    Time Limit: 40 Sec  Memory Limit: 128 MB

    Description

    Blinke 最近喜欢上一个奇怪的游戏。 这个游戏在一个 N*M的棋盘上玩,每个格子有一个数。每次 Blinker会选择两个相邻的格子,并使这两个数都加上 1。 
    现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同
    一个数则输出-1。

    Input

    输入的第一行是一个整数T,表示输入数据有T轮游戏组成。 
    每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数。 
    接下来有N行,每行 M个数。

    Output

    对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出-1。

    Sample Input

    2

    2 2

    1 2

    2 3

    3 3

    1 2 3

    2 3 4

    4 3 2

    Sample Output

    2

    -1

    HINT【数据范围】

    对于30%的数据,保证  T<=10,1<=N,M<=8

    对于100%的数据,保证  T<=10,1<=N,M<=40,所有数为正整数且小于1000000000

    题解

    首先,我们可以很明显的看出最后形成的数(x)一定具有单调性,所以我们可以二分(x)

    那么如何写(check(x))呢?
    首先我们可以将棋盘黑白染色,之后再将黑色与(T)相连,将白色与(S)相连,再将黑白色之间连接。这样就可以用一个最大流来解决。
    因为每个数字都要变为(x),所以可以将节点与(S)(T)相连的边权设为(x-val),之后我们可以将黑白棋子之间的边权设为(inf)(其实边权值可以算出,不过由于之前已经限制了边权,所以这里可以不用处理边权)。
    因为这个最大流其实就是加了多少次,所以最大流应该等于所有数字与x之差之和的一半。这样,我们就可以写出(check)了。

    另外,因为(Cnt[0]*x - Sum[0]=Cnt[1]*x - Sum[1])(Cnt)表示黑白棋格的个数,(Sum)表示黑白棋格各自的和,所以当(Cnt[0] eq Cnt[1])时,我们可以直接得出(x),之后直接(check(x))就好了。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
     
    typedef long long LL;
    const int MS = 100000, N = 42;
    const LL inl = 1LL << 55;
     
    int ina; char inc, inb[1<<16], *ins = inb, *ine = inb; bool insign;
    inline char getc() {
        if (ins == ine) ine = (ins = inb) + fread(inb, 1, 1 << 16, stdin);
        return (ins == ine) ? EOF : *ins++;
    }
    inline int geti() {
        insign = false;
        while ((inc = getc()) < '0' || inc > '9') insign |= inc == '-'; ina = inc - '0';
        while ((inc = getc()) >= '0' && inc <= '9') ina = (ina << 3) + (ina << 1) + inc - '0'; 
        return insign ? -ina : ina;
    }
     
    int mp[N][N], n, m;
    bool col[N][N];
    LL Sum[2], Num[2], mv;
     
    namespace CC {
        int To[MS], Nxt[MS], totE, head[N * N], S, T, d[N * N], dt[N * N];
        LL C[MS];
        inline void Adde(int a, int b, LL c) {
            To[totE] = b; Nxt[totE] = head[a]; C[totE] = c; head[a] = totE++;
            To[totE] = a; Nxt[totE] = head[b]; C[totE] = 0; head[b] = totE++;
        }
         
        const int dx[] = {0,0,-1,1};
        const int dy[] = {1,-1,0,0};
     
        inline int getp(const int &x, const int &y) { return (x-1)*m + y; }
     
        LL dfs(int u, LL flow) {
            if (u==T||!flow) return flow;
            LL res = 0, t;
            for (int i = head[u]; ~i; i = Nxt[i]) 
            if (d[u] == d[To[i]] + 1 && C[i] > 0){
                t = dfs(To[i], min(flow, C[i]));
                C[i] -= t, C[i^1] += t;
                res += t, flow -= t;
                if (!flow) return res;
                if (d[S] > T) return res;
            }
            if (!(--dt[d[u]])) d[S] = T + 1;
            ++dt[++d[u]];
            return res;
        }
         
     
        bool check(LL x) {
            memset(head, -1, sizeof head); totE = 0;
            S = 0, T = getp(n, m) + 1;
            LL t, tot = 0, maxflow = 0;
            for (int i = 1; i <= n; ++i)
              for (int j = 1; j <= m; ++j) {
                  t = x - mp[i][j];
                  if (col[i][j]) Adde(getp(i, j), T, t);
                  else Adde(S, getp(i, j), t);
                  tot += t;
              }
            for (int i = 1; i <= n; ++i)
              for (int j = 1; j <= m; ++j) 
                for (int k = 0; k < 4; ++k) {
                    int _i = i + dx[k], _j = j + dy[k];
                    if (_i < 1 || _i > n || _j < 1 || _j > m) continue;
                    if (!col[i][j]) Adde(getp(i, j), getp(_i, _j), inl);
                }
            memset(d, 0, sizeof d); memset(dt, 0, sizeof dt);
            dt[0] = T + 1;
            while (d[S] < T + 1) maxflow += dfs(S, inl);
            return tot == (maxflow << 1);
        }
     
    }
    void init() {
        memset(col, 0, sizeof col);
        n = geti(), m = geti(); 
        *Sum = Sum[1] = *Num = Num[1] = mv = 0LL;
        bool st = true;
        for (int i = 1; i <= n; ++i) 
          for (int j = 1; j <= m; ++j) {
              if ((mp[i][j] = geti()) > mv) mv = mp[i][j];
              col[i][j] = !col[i][j-1];
              if (j <= 1) col[i][j] = (st = !st);
              ++Num[col[i][j]]; Sum[col[i][j]] += mp[i][j];
          }
    }
     
    #define getskip(a) (a*n*m - Sum[0] - Sum[1]) >> 1
     
    void Work() {
        if (n * m & 1) {
            LL x = Sum[0] - Sum[1];
            if (x >= mv && CC::check(x)) printf("%lld
    ", getskip(x));
            else puts("-1");
        } else {
            LL l = mv, r = inl / 1000, M, ans = -1;
            while (l <= r) {
                M = l + r >> 1;
                if (CC::check(M)) r = (ans = M) - 1;
                else l = M + 1;
            }
            if (~ans) printf("%lld
    ", getskip(ans));
            else puts("-1");
        }
    }
     
    int main() {
        for (int T = geti(); T; --T) {
            init(); Work();
        }
        return 0;
    }
    
  • 相关阅读:
    [NOIP模拟赛][贪心]奶牛晒衣服.
    BZOJ3750: [POI2015]Piecz
    BZOJ2348 [Baltic 2011]Plagiarism
    高精度乘法【高乘高
    codevs 1215 迷宫
    变量交换
    a+b问题与圆柱体表面积的计算
    算数表达式的练习
    [bzoj1070][SCOI2007]修车[ 网络流]
    [bzoj2502]清理雪道[上下界网络流]
  • 原文地址:https://www.cnblogs.com/cycleke/p/5886418.html
Copyright © 2020-2023  润新知