SCOI2012——奇怪的游戏(最大流+二分检验)

来源:互联网 发布:2015年双11淘宝交易额 编辑:程序博客网 时间:2024/06/05 08:04
2756: [SCOI2012]奇怪的游戏

Time Limit: 40 Sec  Memory Limit: 128 MB
Submit: 490  Solved: 114
[Submit][Status][Discuss]
Description

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

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

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

Sample Input

2 2 
1 2 
2 3 
3 3 
1 2 3 
2 3 4 
4 3 2 
Sample Output


-1 
HINT

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

Source

day2

解析:

         将所有格子进行黑白染色。。。因为每次对相邻的两个格子加1,所以相邻的两个格子肯定属于二分图的两边。。。

         然后一边的格子连源点,容量为我们二分出的所有格子最后变成的值x-map[i][j],另一边同样连汇点。。。中间相邻的格子连容量无穷的边。。。

         以上是网络流建图部分。。。

         首先,如果有奇数个格子。。。计算出黑白格子总和的差值diff,如果diff<maxw输-1

         否者,跑最大流,如果能跑满,即flow(diff)==(diff*n*m-sum)>>1;输出等式右边部分,否者-1.。。

         如果有偶数个格子。。。

         我们可以证明当所有格子最后变成一个数成立,那么大于这个数的所有情况都成立。。。

         于是很容易想到二分,分到一个值,如果满足就在左区间找否者就在右区间找。。。

         注意本题会超int

         ps:据说本题卡递归的sap,其实不是。。。我就是用递归sap,但需要小优化一下。。。要把sap的 if(d[0]>=l+2)return res;这句话放到循环里面

                 这儿太坑了,整整Debug了两天。。。

代码:

/**************************************************************    Problem: 2756    User: jianing    Language: C++    Result: Accepted    Time:11884 ms    Memory:3192 kb****************************************************************/ #include<cstdio>#include<algorithm>#include<cmath>#include<cstring>using namespace std;typedef long long LL;const LL inf=0x3f3f3f3f3f3f3f3fll;#define most 1000000000 int n,m,l=0,e=0;int map[50][50],val[2510];struct node{    int u,v,next;    LL c;}edge[100000];int head[100000];int d[2500],sumd[2500];int maxx;LL sum1,sum2; void read(){    freopen("game.in","r",stdin);    freopen("game.out","w",stdout);} void add(int u,int v,LL c){    edge[e].u=u;    edge[e].v=v;    edge[e].c=c;    edge[e].next=head[u];    head[u]=e++;    edge[e].u=v;    edge[e].v=u;    edge[e].c=0;    edge[e].next=head[v];    head[v]=e++;} LL sap(int u,LL flow){    if(u==l+1)return flow;    LL res=0;    for(int i=head[u];i!=-1;i=edge[i].next)    {        int v=edge[i].v;        if(edge[i].c && d[u]==d[v]+1)        {            LL t=sap(v,(LL)min(edge[i].c,(flow-res)));            res+=t;            edge[i].c-=t;edge[i^1].c+=t;            if(res==flow)return res;            if(d[0]>=l+2)return res;//本来放在下面,T哭了        }                           //    }                               //    sumd[d[u]]--;                   //就这儿    if(sumd[d[u]]==0)d[0]=l+2;    sumd[++d[u]]++;    return res;} LL flow(LL x){    memset(head,-1,sizeof(head));    memset(edge,0,sizeof(edge));    e=0;l=n*m;int t=n*m+1;    for(int i=1;i<=n;i++)        for(int j=1;j<=m;j++)        {            if((i+j)&1)            {                add(0,m*(i-1)+j,x-map[i][j]);                if(i>1)add(m*(i-1)+j,m*(i-2)+j,inf);                if(i<n)add(m*(i-1)+j,m*i+j,inf);                if(j>1)add(m*(i-1)+j,m*(i-1)+j-1,inf);                if(j<m)add(m*(i-1)+j,m*(i-1)+j+1,inf);            }            else add(m*(i-1)+j,t,x-map[i][j]);        }    memset(d,0,sizeof(d));    memset(sumd,0,sizeof(sumd));    sumd[0]=l+2;    LL ans=0;    while(d[0]<=l+1)        ans+=sap(0,inf);    return ans;}  void check(){    LL best=inf;    LL ll=(LL)maxx,rr=inf;    while(ll<rr)    {        LL mid=(ll+rr)>>1;        LL ans=flow(mid);        LL tmp=mid*n*m-sum1-sum2;        if(tmp==ans*2)        {            if(ans>0)best=min(best,ans);            rr=mid;        }        else ll=mid+1;    }    printf("%lld\n",best);} LL max(LL a,LL b){    return a>b?a:b;} void work(){    e=0;maxx=0;    sum1=0;sum2=0;int cnt1=0,cnt2=0;    memset(map,0,sizeof(map));    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)        for(int j=1;j<=m;j++)        {            scanf("%d",&map[i][j]);            maxx=max(maxx,map[i][j]);            if((i+j)&1)            {                sum1+=map[i][j];                cnt1++;            }            else            {                sum2+=map[i][j];                cnt2++;            }        }    if(cnt1!=cnt2)    {        if((sum1-sum2)%(cnt1-cnt2)!=0)printf("-1\n");        LL diff=(sum1-sum2)/(cnt1-cnt2);        if(diff<maxx)        {            printf("-1\n");            return ;        }        else        {            LL ans=flow(diff);            LL tmp=diff*n*m-sum1-sum2;            if(tmp==ans*2)printf("%lld\n",ans);            else printf("-1\n");        }    }    else check();} int main(){   // read();    int test;    scanf("%d",&test);    while(test--)    work();    return 0;}




原创粉丝点击