bzoj2756 [SCOI2012]奇怪的游戏

来源:互联网 发布:约瑟夫环的数组实现 编辑:程序博客网 时间:2024/05/17 01:48

Description

Blinker最近喜欢上一个奇怪的游戏。
这个游戏在一个 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

Source

http://cxjyxx.me/?p=223

cxjyxx_me:

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

对棋盘进行黑白染色
设黑格个数为num1 数值和为sum1
设白格个数为num1 数值和为sum1

设最后都变为x

num1 * x – sum1 = num2 * x – sum2
x = (sum1 – sum2) / (num1 – num2)
当num1 ≠ num2时 可以解出 x 再用网络流check即可

对于num1 = num2时 可以发现 对于一个合法的x k>=x都是一个合法的解
因为num1 = num2 => (num1 + num2) % 2 == 0 可以构造一层的满覆盖
所以可以二分x 然后用网络流check

建图:
如果点k为白
建边(s, k, x – v[k])
如果为黑
建边(k, t, x – v[k])
对相邻点u、v (u为白)
建边 (u, v, inf)

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<set>#include<ctime>#include<vector>#include<queue>#include<algorithm>#include<map>#include<cmath>#define M 20005#define inf (1LL<<50)#define pa pair<int,int>#define ll long long #define p(x,y) (x-1)*m+yusing namespace std;inline int read(){    int x = 0, f = 1; char ch = getchar();    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }    while (ch >= '0' && ch <= '9') { x = x * 10 + ch-'0'; ch = getchar(); }    return x * f;}ll s0,s1;int c0,c1;int test,n,m,cnt,S,T;int xx[4] = {0, 0, 1, -1};int yy[4] = {1, -1, 0, 0};int a[45][45];int head[2005],h[2005],q[2005],cur[2005];bool color[45][45];struct edge{    int to, next;    ll v;}e[20005];void ins(int u, int v, ll w){    e[++ cnt].next = head[u];    head[u] = cnt;    e[cnt].to = v;    e[cnt].v = w;}void insert(int u, int v, ll w){    ins(u, v, w);    ins(v, u, 0);}ll dfs(int x, ll f){    if(x == T) return f;    ll used = 0, w;    for(int i = head[x]; i; i = e[i].next)    {        int y = e[i].to;        if(h[y] == h[x] + 1 && e[i].v)        {            w = dfs(y, min(e[i].v, f - used));            e[i].v -= w;            if(e[i].v) cur[x] = i;            e[i^1].v += w;            used += w;            if(f == used) return f;        }     }    if(used == 0) h[x] = -1;    return used;}int bfs(){    int t = 0, w = 1;     memset(h, -1, sizeof(h));    q[0] = S;    h[S] = 0;    while(t != w)    {        int now = q[t ++];        if(t == M) t = 0;        for(int i = head[now]; i; i = e[i].next)        {            int y = e[i].to;            if(h[y] == -1 && e[i].v)            {                h[y] = h[now] + 1;                q[w ++] = y;                if(w == M) w = 0;            }        }    }    if(h[T] == -1) return 0;    else return 1;}ll dinic(){    ll ans = 0ll;    while(bfs())    {        for(int i = S; i <= T; i ++)            cur[i] = head[i];        ans += dfs(S, inf);    }    return ans;}bool check(ll x){    memset(head,0,sizeof(head));    cnt = 1;    S = 0;    T = n * m + 1;    ll tot = 0;    for(int i = 1; i <= n; i ++)        for(int j = 1; j <= m; j ++)            if(color[i][j])            {                insert(S, p(i,j), x - a[i][j]);                tot += x - a[i][j];                for(int k = 0; k < 4; k ++)                {                    int nowx = i + xx[k], nowy = j + yy[k];                    if(nowx < 1 || nowy < 1 || nowx > n || nowy > m)continue;                    insert(p(i, j),                    p(nowx, nowy), inf);                }            }            else insert(p(i, j), T, x - a[i][j]);    if(dinic() == tot)return 1;    return 0;}int main(){    test = read();    while(test --)    {        c0 = c1 = s0 = s1 = 0;        n = read();        m = read();        int mx = 0;        for(int i = 1; i <= n; i ++)            for(int j = 1; j <= m; j ++)            {                a[i][j] = read(), color[i][j] = (i + j) & 1;                mx = max(mx, a[i][j]);            }        for(int i = 1; i <= n; i ++)            for(int j = 1; j <= m; j ++)                if(color[i][j]) s1 += a[i][j], c1 ++;                else s0 += a[i][j], c0 ++;        if(c0 != c1)        {            ll x = (s0 - s1) / (c0 - c1);            if(x >= mx)                if(check(x))                {                    printf("%lld\n",x * c1 - s1);                    continue;                }            puts("-1");        }        else         {            ll l = mx, r = inf;            while(l <= r)            {                ll mid = (l + r) >> 1;                if(check(mid)) r = mid - 1;                else l = mid + 1;            }            printf("%lld\n",(ll)l * c1 - s1);        }    }       return 0;}
0 0