zoukankan      html  css  js  c++  java
  • [BZOJ 2756] 奇怪的游戏

    Link:https://www.lydsy.com/JudgeOnline/problem.php?id=2756

    Algorithm:

    比较新颖的题目

    首先发现是对矩阵中相邻两数进行操作    <----->    想到黑白染色

    于是Delta(BlackSum)=Delta(WhiteSum)

    由于最后要变成同一个数X,

    那么在BlackNum=WhiteNum时,

    1、如果WhiteSum!=BlackSum,显然无解

    2、如果WhiteSum==BlackSum时,由于矩阵能被1*2的矩形完全覆盖,那么X是否满足要求是具有单调性

          于是我们二分X,进行判断即可

    在WhiteNum!=BlackNum时,

    显然可以得到XWhiteNumWhiteSum=XBlackNumBlackSum

    移项后显然可以化简出X=(BlackSumWhiteSum)  /   (BlackNumWhiteNum)

    验证这个X即可

    最大问题,如何判断X是否可行,

    一开始得出的性质:Delta(BlackSum)=Delta(WhiteSum),符合流守恒性(流入和流出量相同)

    于是我们进行可以将黑点看作一边,而将白点看作另外一边,网络流建图:

    S-->白点,CAP为X-val[i][j]

    黑点-->T,CAP为X-val[i][j]

    相邻的白点-->黑点,CAP为INF

    判断能否满流即可

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    #define pos(x,y) (x-1)*m+y
    typedef pair<int,int> P;
    typedef long long ll;
    const int MAXN=50;
    const ll INF=1ll<<50;
    
    ll test,n,m,col[MAXN][MAXN],dat[MAXN][MAXN],iter[MAXN*MAXN],level[MAXN*MAXN];
    ll sum1,sum2,cnt1,cnt2,mx,S,T;
    
    struct edge
    {
        ll to,cap,rev;
    };
    vector<edge> G[MAXN*MAXN];
    
    void add_edge(int from,int to,ll cap)
    {
        G[from].push_back(edge{to,cap,G[to].size()});
        G[to].push_back(edge{from,0,G[from].size()-1});
    }
    
    bool bfs()
    {
        memset(level,-1,sizeof(level));
        queue<int> que;que.push(S);
        
        level[S]=0;
        while(!que.empty())
        {
            int t=que.front();que.pop();
            for(int i=0;i<G[t].size();i++)
            {
                edge e=G[t][i];
                if(e.cap>0 && level[e.to]==-1)
                {
                    level[e.to]=level[t]+1;
                    que.push(e.to);
                }
            }
        }
    }
    
    ll dfs(int cur,int T,ll f)
    {
        if(cur==T) return f;
        
        for(ll &i=iter[cur];i<G[cur].size();i++)
        {
            edge &e=G[cur][i];
            if(level[e.to]==level[cur]+1 && e.cap>0)
            {
                ll d=dfs(e.to,T,min(f,e.cap));
                if(d>0)
                {
                    e.cap-=d;
                    G[e.to][e.rev].cap+=d;
                    return d;
                }
            }
        }
        return 0;
    }
    
    ll dinic()
    {
        ll ret=0;
        while(true)
        {
            memset(iter,0,sizeof(iter));
            bfs();if(level[T]==-1) break;
            ll f;
            while((f=dfs(S,T,INF))>0) ret+=f;  //这里括号不能排错啊
        }
        return ret;
    }
    
    int dx[]={0,0,1,-1};
    int dy[]={1,-1,0,0};
    
    bool check(ll tar)
    {
        S=0;T=n*m+1;ll ret=0;
        for(int i=0;i<MAXN*MAXN;i++) G[i].clear();
        for(int i=1;i<=n;i++)  //建图
            for(int j=1;j<=m;j++)
            {
                if(col[i][j]) add_edge(S,pos(i,j),tar-dat[i][j]),ret+=tar-dat[i][j];
                else {add_edge(pos(i,j),T,tar-dat[i][j]);continue;}
                for(int k=0;k<4;k++)
                {
                    int fx=i+dx[k],fy=j+dy[k];
                    if(fx>=1 && fx<=n && fy>=1 && fy<=m)
                        add_edge(pos(i,j),pos(fx,fy),INF);
                }
            }
        
        ll MAXFLOW=dinic(); //dinic
        return (ret==MAXFLOW);
    }
    
    int main()
    {
        cin >> test;
        while(test--)
        {
            cin >> n >> m;mx=0;
            cnt1=cnt2=sum1=sum2=0;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                {
                    cin >> dat[i][j];
                    col[i][j]=(i+j)&1;mx=max(mx,dat[i][j]);
                    if(col[i][j]) sum1+=dat[i][j],cnt1++;
                    else sum2+=dat[i][j],cnt2++;
                }
            
            if(cnt1!=cnt2)
            {
                ll tar=(sum1-sum2)/(cnt1-cnt2);
                if(tar<mx){puts("-1");continue;}
                
                if(check(tar))
                {
                    cout << tar*cnt1-sum1 << endl;
                    continue;
                }
                puts("-1");
            }
            else
            {
                if(sum1!=sum2){puts("-1");continue;}
                
                ll l=mx,r=INF;
                while(l<=r)
                {
                    ll mid=(l+r)>>1;
                    if(check(mid)) r=mid-1;
                    else l=mid+1;
                }
                if(r==INF) puts("-1");   //如果到INF都无解,则说明就是无解,要特判,但BZOJ上数据比较水(黄学长的标程是错的)
                else cout << 1ll*(l*cnt1-sum1) << endl;
            }
        }
        return 0;
    }

    Review:

    1、看到这种对相邻格子同时操作的题目,想到黑白染色

    2、求极值,观察值的可行性是否具有单调性,从而能否二分

    3、当数据能分为两组,且具有流守恒性时

          考虑使用网络流

    4、调试:

    (1)有多个括号要留心每个括号的位置啊,多看几遍

    while((f=dfs(S,T,INF))>0)

    这里的>0一开始放到第2个括号里了,结果f每次就都是0或1了……囧,调了1h

    (2)像网络流这样执行过一边就会对原数组产生影响的模块,

             执行过一次后想调试/再次调用时,不能再执行一遍,将第一次结果存储即可

    (3)由于模板中大部分数据都是int,如果有long long时要特别注意修改原模板中的int

            惨案:

    void add_edge(int from,int to,ll cap)
  • 相关阅读:
    Binary Search Tree Iterator 解答
    Invert Binary Tree 解答
    Min Stack 解答
    Trapping Raining Water 解答
    Candy 解答
    Jump Game II 解答
    Implement Hash Map Using Primitive Types
    Gas Station 解答
    Bucket Sort
    HashMap 专题
  • 原文地址:https://www.cnblogs.com/newera/p/9070590.html
Copyright © 2011-2022 走看看