2756: [SCOI2012]奇怪的游戏
Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 2510 Solved: 665
[ 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
蒟蒻是一种境界- -
--------------------------------------------------------------------------------------------------------------------------------------------------
真的好想乱搞,看数据范围不算太大- -
but毫无头绪
参考了各种资料+yy的补充,得到网上普遍的一种做法
---考虑最后所有格子都变成了x ,并且把棋盘黑白染色,
黑色的权值为sum1,格子数cnt1 白色的权值sum2,格子数cnt2
所有的都变成了x
则cnt1*x-sum1=cnt2*x-sum2=变化的权值
x=(sum1-sum2)/(cnt1-cnt2)
x需要>=max(s[i][j])
然后讨论:
①cnt1!=cnt2的时候,解出x的值,check
②cnt1==cnt2的时候,如果sum1!=sum2.不成立(代入上述方程无解)
否则,解有无穷多,找满足题意得最小的解x,二分+check
经过网上题解的洗脑+自己的画图分析,得到这样一个关系式
如果x满足条件,则(cnt1+cnt2)*x-maxflow*2=sum1+sum2
并且如果x,满足,那么x+1也满足(这种情况下cnt1==cnt2棋盘为
偶数,一定可以分成n/2个小的长方体)
接下来就check好了
关于建边:
将棋盘黑白染色之后,黑色的棋子和S连一条x-ss[i][j]的边,
和四边白色连一条inf的边,白色和T连一条x-ss[i][j]的边
然后就是写最大流的问题了,不知道为什么网上说这道题卡Dinic,不过我写的一般是听说是ISAP的东东,在当前一直更新,发现没被卡掉- -
还有就是long long 的问题,以及inf要大
总结一下:对于棋盘问题(尤其是相邻棋子问题)考虑黑白染色,然后就视情况而定。
这里有一点二分的意思,所以考虑是否往二分上靠(主要看数据估算设计算法的复杂度)
太蒟蒻了- -
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#define LL long long
using namespace std;
/*---考虑最后所有格子都变成了x ,并且把棋盘黑白染色,
黑色的权值为sum1,格子数cnt1 白色的权值sum2,格子数cnt2
所有的都变成了x
则cnt1*x-sum1=cnt2*x-sum2=变化的权值
x=(sum1-sum2)/(cnt1-cnt2)
x需要>=max(s[i][j])
然后讨论:
①cnt1!=cnt2的时候,解出x的值,check
②cnt1==cnt2的时候,如果sum1!=sum2.不成立(代入上述方程无解)
否则,解有无穷多,找满足题意得最小的解x,二分+check
经过网上题解的洗脑+自己的画图分析,得到这样一个关系式
如果x满足条件,则(cnt1+cnt2)*x-maxflow*2=sum1+sum2
并且如果x,满足,那么x+1也满足(这种情况下cnt1==cnt2棋盘为
偶数,一定可以分成n/2个小的长方体)
接下来就check好了
关于建边:
将棋盘黑白染色之后,黑色的棋子和S连一条x-ss[i][j]的边,
和四边白色连一条inf的边,白色和T连一条x-ss[i][j]的边
*/
const LL inf=0x3f3f3f3f3f3f3f3fLL;
LL n,m;
LL ss[50][50];
LL sum1,sum2,cnt1,cnt2;
LL maxz;
LL S,T;
struct node
{
LL v,cap,next;
}e[2100000+20];
LL head[2000];
LL dis[2000];
LL k;
void add(LL u,LL v,LL cap)
{
e[k].v=v;
e[k].cap=cap;
e[k].next=head[u];
head[u]=k++;
e[k].v=u;
e[k].cap=0;
e[k].next=head[v];
head[v]=k++;
}
LL dx[]={0,0,1,-1};
LL dy[]={1,-1,0,0};
bool bfs()
{
queue<LL >q;
q.push(S);
memset(dis,-1,sizeof(dis));
dis[S]=0;
while(!q.empty())
{
LL u=q.front();
q.pop();
for(LL i=head[u];i!=-1;i=e[i].next)
{
LL v=e[i].v;
if(dis[v]==-1&&e[i].cap>0)
{
dis[v]=dis[u]+1;
if(v==T)return true;
q.push(v);
}
}
}
return false;
}
LL dfs(LL u,LL low)
{
if(u==T||low==0)return low;
LL ret=0;
for(LL i=head[u];i!=-1;i=e[i].next)
{
LL v=e[i].v;
if(dis[v]==dis[u]+1)
{
LL f=dfs(v,min(low,e[i].cap));
e[i].cap-=f;
e[i^1].cap+=f;
low-=f;
ret+=f;
if(low==0)return ret;
}
}
dis[u]=-1;
return ret;
}
LL work()
{
LL ans=0;
while(bfs())
{
LL x;
if(x=dfs(S,inf))ans+=x;
}
return ans;
}
bool check(LL x)
{
memset(head,-1,sizeof(head));
for(LL i=1;i<=n;i++)
{
for(LL j=1;j<=m;j++)
{
LL id=(i-1)*m+j;
if((i+j)&1)//白色
{
add(id,T,x-ss[i][j]);
}
else//黑色
{
add(S,id,x-ss[i][j]);
for(LL l=0;l<4;l++)
{
LL nx=i+dx[l];
LL ny=j+dy[l];
if(nx>=1&&nx<=n&&ny>=1&&ny<=m)
{
LL nid=(nx-1)*m+ny;
add(id,nid,inf);
}
}
}
}
}
//建边完成
return (cnt1+cnt2)*x-work()*2==sum1+sum2;
}
int main()
{
LL t;
scanf("%lld",&t);
while(t--)
{
sum1=sum2=cnt1=cnt2=0;
maxz=0;
k=0;
scanf("%lld%lld",&n,&m);
S=0,T=n*m+1;
for(LL i=1;i<=n;i++)
{
for(LL j=1;j<=m;j++)
{
scanf("%lld",&ss[i][j]);
maxz=max(maxz,ss[i][j]);
if((i+j)&1)//白棋
{
sum2+=ss[i][j];
cnt2++;
}
else
{
sum1+=ss[i][j];
cnt1++;
}
}
}
if(cnt1==cnt2)
{
if(sum1!=sum2)printf("-1\n");
else//二分+check
{
LL l=maxz,r=inf;
while(l<r)
{
LL mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%lld\n",((cnt1+cnt2)*l-(sum1+sum2))/2);
}
}
else
{
LL x=(sum1-sum2)/(cnt1-cnt2);
if(x<maxz||!check(x))printf("-1\n");
else printf("%lld\n",((cnt1+cnt2)*x-(sum1+sum2))/2);
}
}
return 0;
}