zoukankan      html  css  js  c++  java
  • 洛谷5038 [SCOI2012]奇怪的游戏(二分+网络流+判断奇偶)

    寒假的时候就听过这个题。但是一直没有写。
    qwq

    首先,我们发现题目中的图是个网格图,然后每次可以将相邻两个格子加一。
    很容易就想到是黑白染色。那么每次操作,就相当于同时操作一个白点,一个黑点。
    我们会发现,这样其实到最终局面的时候,黑点和白点所加的差是相等的,也就是说,我们假设黑点的个数是(num1),权值和是(sum1),白点的个数是(num2),权值和是(sum2)。若最终局面的数字是(x)

    [num1 imes x - sum1 =num2 imes x - sum2 ]

    那么$$x = frac{sum1-sum2}{num1-num2}$$

    比较容易发现的是,如果(num1!=num2),那我们可以直接求出来这个(x),然后(check)一下就好。

    那如果(num1==num2)呢?
    由于两个格子的数目是一样的,所以,只要我们能到达(x),我们也一定可以通过一些+的操作,使得局面能到达(x+1)。那么这时候就可以直接二分+(check)了。

    那么该如果check呢?
    其实也比较简单。
    (S ightarrow白点,黑点 ightarrow T)

    流量是他们的权值和二分的值的差,然后黑白点之间的流量是(inf),直接跑(dinic)即可。

    记得最终的(ans)是操作次数,不是最小的合成的数。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define mk make_pair
    #define ll long long
    #define int long long
    using namespace std;
    inline int read()
    {
      int x=0,f=1;char ch=getchar();
      while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
      while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
      return x*f;
    }
    const int maxn = 5010;
    const int maxm = 2e6+1e2;
    const int inf = 2e18;
    int point[maxn],nxt[maxm],to[maxm];
    int val[maxm],cnt=1,n,m;
    int a[100][100];
    int col[100][100];
    void addedge(int x,int y,int w)
    {
    	nxt[++cnt]=point[x];
    	to[cnt]=y;
    	point[x]=cnt;
    	val[cnt]=w;
    }
    void insert(int x,int y,int w)
    {
      addedge(x,y,w);
      addedge(y,x,0);
    }
    int h[maxn];
    int s,t;
    queue<int> q;
    int mx;
    int tmp;
    bool bfs(int s)
    {
    	  memset(h,-1,sizeof(h));
    	  h[s]=0;
    	  q.push(s);
    	  while (!q.empty())
    	  {
    	  	int x=q.front();
    	  	q.pop();
    	  	for (int i=point[x];i;i=nxt[i])
    	  	{
    	  		int p  = to[i];
    	  		if (h[p]==-1 && val[i]>0)
    	  		{
    	  			q.push(p);
    	  			h[p]=h[x]+1;
    			  }
    		  }
    	  }
    	  if (h[t]==-1) return false;
    	  return true;
    }
    int dfs(int x,int low)
    {
    	if (x==t || low==0) return low;
    	int totflow=0;
    	for (int i=point[x];i;i=nxt[i])
    	{
    		int p = to[i];
    		if (h[p]==h[x]+1 && val[i]>0)
    		{
    			int tmp = dfs(p,min(low,val[i]));
    			val[i]-=tmp;
    			val[i^1]+=tmp;
    			totflow+=tmp;
    			low-=tmp;
    			if(low==0) return totflow;
    		}
    	}
    	if(low>0) h[x]=-1;
    	return totflow;
    }
    int dinic()
    {
    	int ans=0;
    	while (bfs(s))
    	{
    		ans=ans+dfs(s,inf); 
    	}
    	return ans;
    }
    void init()
    {
      cnt=1;
      memset(point,0,sizeof(point));
    }
    int getnum(int x,int y)
    {
    	return (x-1)*m+y;
    }
    int dx[5]={0,-1,0,1,0};
    int dy[5]={0,0,-1,0,1};
    bool check(int mid)
    {
    	init();
    	if (mid<mx) return false;
    	int sum1=0,sum2=0;
    	for (int i=1;i<=n;i++)
    	  for (int j=1;j<=m;j++)
    	  {
    	  	if (col[i][j]==1) insert(s,getnum(i,j),mid-a[i][j]),sum1+=mid-a[i][j];
    	  	else insert(getnum(i,j),t,mid-a[i][j]),sum2+=mid-a[i][j];
    	  	//sum+=mid-a[i][j];
    	  }
    	if (sum1!=sum2) return false;
    	for (int i=1;i<=n;i++)
    	{
    		for (int j=1;j<=m;j++)
    		{
    			for (int k=1;k<=4;k++)
    			{
    				int ii = i+dx[k];
    				int jj = j+dy[k];
    				if(ii<=0 || ii>n || jj<=0 || jj>m) continue;
    				if (col[i][j]==2) continue;
    				insert(getnum(i,j),getnum(ii,jj),inf);
    			}
    		}
    	}
    	tmp=dinic();
    	return (tmp==sum1);
    }
    signed main()
    {
      int T=read();
      while (T--)
      {
      	  init();
    	  n=read();m=read();
    	  mx=0;
    	  s=maxn-10;
    	  t=s+1;
      	  for (int i=1;i<=n;i++)
      	    for (int j=1;j<=m;j++)
    		{
    		   if ((i+j)&1)
    		     col[i][j]=1;
    		   else col[i][j]=2;
    		   a[i][j]=read();	
    		   mx=max(a[i][j],mx);
    		} 
    	 int num1=0,num2=0;
    	 int sum1=0,sum2=0;
    	 for (int i=1;i<=n;i++)
    	 {
    	 	for (int j=1;j<=m;j++)
    	 	{
    	 		if (col[i][j]==1)
    	 		{
    	 			num1++;
    	 			sum1+=a[i][j];
    			}
    			else
    			{
    				num2++;
    				sum2+=a[i][j];
    			}
    		 }
    	 }
    	 int ans = -1;
    	 int l=0,r=1e18;
    	 //cout<<1<<endl;	
    	 if (num1==num2)
    	 {
    	 	while (l<=r)
    	 	{
    	 		int mid = (l+r) >> 1;
    	 		if (check(mid)) ans=tmp,r=mid-1;
    	 		else l=mid+1;
    	 		//cout<<1<<endl;
    		}
    	 }
    	 else
    	 {
    	 	int now = (sum1-sum2)/(num1-num2);
    	 	if (check(now)) ans=tmp;
    	 }
    	 cout<<ans<<"
    ";
      }
      return 0;
    }
    
    
  • 相关阅读:
    IplImage, CvMat, Mat 的关系
    neon memory copy
    基于v4l2的webcam应用, 本地预监
    makefile写法实例
    Ubuntu 12.04 使用Eclipse搭建C/C++编译环境
    xapp1167与TRD14.4 关系
    v3学院带你一次性认清UART、RS-232、RS-422、RS-485的区别
    v3学院教你学习-task和function的异同
    寒假参加V3
    FPGA培训学习心得
  • 原文地址:https://www.cnblogs.com/yimmortal/p/10168938.html
Copyright © 2011-2022 走看看