• BZOJ2756:[SCOI2012]奇怪的游戏(最大流,二分)


    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

    Solution

    emmm这个题真的有毒啊……INF改成8e8才过……最慢的点跑2s……幸亏BZOJ算总时间
    首先看到这个数据范围十有八九网络流没跑了……
    先将格子黑白染色,考虑一通操作成功后每个格子的数都是v,若:
    1、n*m是奇数。设黑白格子个数为num[0/1],和为sum[0/1],那么$num0*x-sum0=num1*x-sum1$,因为每次操作肯定会给一个黑点和一个白点加。
    2、n*m是偶数。如果最终答案是v满足的话,那么显然最终答案是v+1肯定也是可以满足的。这个就可以二分了。
    现在的问题转换为判断是否可行。
    s-black,容量为v-a[i][j]
    white-e,容量为v-a[i][j]
    black-white, 容量为INF。
    怎么理解呢?把INF边看成给两端的数各自加一,而INF边连着的两端的边又限制了INF边的使用次数。

    Code 

      1 #include<iostream>
      2 #include<cstring>
      3 #include<cstdio>
      4 #include<queue>
      5 #define N (2001)
      6 #define INF (8000000000LL)
      7 #define LL long long
      8 using namespace std;
      9 
     10 struct Edge{LL to,next,flow;}edge[N<<4];
     11 LL T,n,m,s,e=1999,Depth[N],num[2],sum[2],maxn,cnt;
     12 LL head[N],num_edge,a[N][N],col[N][N],id[N][N];
     13 LL dx[6]={0,1,-1,0,0},dy[6]={0,0,0,1,-1};
     14 queue<LL>q;
     15 
     16 void add(LL u,LL v,LL l)
     17 {
     18     edge[++num_edge].to=v;
     19     edge[num_edge].next=head[u];
     20     edge[num_edge].flow=l;
     21     head[u]=num_edge;
     22 }
     23 
     24 LL Dfs(LL x,LL low)
     25 {
     26     if (x==e || low==0) return low;
     27     LL f=0,Min=0;
     28     for (int i=head[x]; i; i=edge[i].next)
     29         if (edge[i].flow && Depth[edge[i].to]==Depth[x]+1 && (Min=Dfs(edge[i].to,min(low,edge[i].flow))))
     30         {
     31             edge[i].flow-=Min;
     32             edge[((i-1)^1)+1].flow+=Min;
     33             f+=Min; low-=Min;
     34             if (!low) break;
     35         }
     36     if (!f) Depth[x]=-1;
     37     return f;
     38 }
     39 
     40 bool Bfs(LL s,LL e)
     41 {
     42     memset(Depth,0,sizeof(Depth));
     43     Depth[s]=1; q.push(s);
     44     while (!q.empty())
     45     {
     46         LL x=q.front(); q.pop();
     47         for (int i=head[x]; i; i=edge[i].next)
     48             if (!Depth[edge[i].to] && edge[i].flow)
     49             {
     50                 Depth[edge[i].to]=Depth[x]+1;
     51                 q.push(edge[i].to);
     52             }
     53     }
     54     return (Depth[e]!=0);
     55 }
     56 
     57 bool Dinic(LL s,LL e)
     58 {
     59     while (Bfs(s,e)) Dfs(s,INF);
     60     for (int i=head[s]; i; i=edge[i].next)
     61         if (edge[i].flow!=0) return false;
     62     for (int i=head[e]; i; i=edge[i].next)
     63         if (edge[((i-1)^1)+1].flow!=0) return false;
     64     return true;
     65 }
     66 
     67 bool check(LL v)
     68 {
     69     memset(head,0,sizeof(head)); num_edge=0;
     70     memset(edge,0,sizeof(edge));
     71     for (int i=1; i<=n; ++i)
     72         for (int j=1; j<=m; ++j)
     73         {
     74             if (col[i][j]) add(s,id[i][j],v-a[i][j]),add(id[i][j],s,0);
     75             else add(id[i][j],e,v-a[i][j]),add(e,id[i][j],0);
     76             if (!col[i][j]) continue;
     77             for (int k=1; k<=4; ++k)
     78             {
     79                 LL x=i+dx[k], y=j+dy[k];
     80                 if (x<1 || x>n || y<1 || y>m) continue;
     81                 add(id[i][j],id[x][y],INF),add(id[x][y],id[i][j],0);
     82             }
     83         }
     84     return Dinic(s,e);
     85 }
     86 
     87 int main()
     88 {
     89     scanf("%lld",&T);
     90     while (T--)
     91     {
     92         scanf("%lld%lld",&n,&m);
     93         sum[0]=sum[1]=maxn=cnt=0;
     94         if (n*m%2) num[1]=n*m/2+1,num[0]=n*m/2;
     95         else num[1]=num[0]=n*m/2;
     96         for (int i=1; i<=n; ++i)
     97             for (int j=1; j<=m; ++j)
     98             {
     99                 scanf("%lld",&a[i][j]);
    100                 id[i][j]=++cnt;
    101                 maxn=max(maxn,a[i][j]);
    102                 if (i%2==j%2) col[i][j]=1;
    103                 sum[col[i][j]]+=a[i][j];
    104             }
    105         if (n*m%2)
    106         {
    107             LL v=(sum[1]-sum[0])/(num[1]-num[0]);
    108             if (check(v)) printf("%lld
    ",(v*n*m-sum[0]-sum[1])/2);
    109             else printf("-1
    ");
    110         }
    111         else
    112         {
    113             LL l=maxn, r=INF, ans=-1;
    114             while (l<=r)
    115             {
    116                 LL mid=(l+r)/2;
    117                 if (check(mid)) r=mid-1,ans=mid;
    118                 else l=mid+1;
    119             }
    120             if (ans==-1) printf("-1
    ");
    121             else printf("%lld
    ",(ans*n*m-sum[0]-sum[1])/2);
    122         }
    123     }
    124 }
  • 相关阅读:
    继承人人前端笔试题
    【转】ASP.NET应用程序生命周期趣谈
    C#中正则表达式的高级应用
    使用C#导入导出数据到Excel
    Server.Transfer详细解释
    防止刷新重复post提交
    程序只运行一次的方法
    注释很全的抽象工厂(没用简单工厂优化)
    利用反射动态调用类成员C#
    编程经历的一些思考
  • 原文地址:https://www.cnblogs.com/refun/p/9537449.html
Copyright © 2020-2023  润新知