您的位置:首页 > 其它

[SCOI2012][BZOJ2756] 奇怪的游戏

2015-07-21 14:28 375 查看

2756: [SCOI2012]奇怪的游戏

Time Limit: 40 Sec Memory Limit: 128 MB
Submit: 1970 Solved: 515
[Submit][Status][Discuss]

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
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)

PS:昨晚研究了下二分图的问题,这也是第一道染色建图的题目,不过真心没有调出来……不知道哪里出了问题,不想继续浪费时间。

未AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define ll long long
#define INF (1LL<<50)
using namespace std;
int S,T,t,n,m,edge=1,dis[2005],q[2005],head[2005],list[30000],next[30000];
ll mx,a[50][50],num1,num2,sum1,sum2,cnt,num[50][50],key[30000];
int dx[4]={-1,0,1,0};
int dy[4]={0,-1,0,1};
void insert(int x,int y,ll z)
{
next[++edge]=head[x];
head[x]=edge;
list[edge]=y;
key[edge]=z;
}
void build_paint()
{
sum1=0;sum2=0;
cnt=0;
for (int i=1;i<=n;i++)
for (int j=1+(i%2==0);j<=m;j+=2)
{
num[i][j]=++cnt;
sum1+=a[i][j];
}
num1=cnt;
for (int i=1;i<=n;i++)
for (int j=1+(i%2==1);j<=m;j+=2)
{
num[i][j]=++cnt;
sum2+=a[i][j];
}
num2=cnt-num1;
}
bool BFS()
{
memset(dis,0xff,sizeof(dis));
dis[0]=1; q[1]=0;
int t=0,w=1,x;
while (t<w)
{
x=q[++t];
for (int i=head[x];i;i=next[i])
if (key[i]&&dis[list[i]]==-1)
{
dis[list[i]]=dis[x]+1;
q[++w]=list[i];
}
}
return dis[T]!=-1;
}
ll find(int x,ll flow)
{
if (x==T) return flow;
ll used=0,w;
for (int i=head[x];i;i=next[i])
if (key[i]&&dis[list[i]]==dis[x]+1)
{
w=find(list[i],min(key[i],flow-used));
key[i]-=w;
key[i^1]+=w;
used+=w;
if (used==flow) return flow;
}
if (!used) dis[x]=-1;
return used;
}
ll dinic()
{
ll ans=0;
while (BFS()) ans+=find(S,INF);
return ans;
}
bool check(ll x)
{
memset(head,0,sizeof(head));
ll tot=0;
int xx,yy;
edge=1;
for (int i=1;i<=n;i++)
for (int j=1+(i%2==0);j<=m;j+=2)
{
tot+=x-a[i][j];
insert(S,num[i][j],x-a[i][j]);
insert(num[i][j],S,0);
for (int k=0;k<4;k++)
{
xx=i+dx[k];
yy=j+dy[k];
if (xx<1||xx>n||yy<1||yy>m) continue;
insert(num[i][j],num[xx][yy],INF);
insert(num[xx][yy],num[i][j],0);
}
}
for (int i=1;i<=n;i++)
for (int j=1+(i%2==1);j<=m;j+=2)
{
insert(num[i][j],T,x-a[i][j]);
insert(T,num[i][j],0);
}
if (dinic()==tot) return 1; return 0;
}
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);

scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&m);
S=0;T=n*m+1;
mx=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
scanf("%lld",&a[i][j]);
mx=max(mx,a[i][j]);
}
build_paint();
if (num1!=num2)
{
ll x=(sum1-sum2)/(num1-num2);
if (x>=mx)
if (check(x))
{
printf("%lld\n",x*num2-sum2);
continue;
}
printf("-1\n");
}
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",num2*l-sum2);
}
}
return 0;
}


想看AC代码的出门左拐hzwer.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: