bzoj2756: [SCOI2012]奇怪的游戏 二分+最大流

本文探讨了一个涉及复杂棋盘黑白染色的问题,重点在于如何通过算法计算不同情况下黑白棋子数量的平衡,以及如何利用Dinic算法解决流量网络问题。通过分析不同条件下的数学模型,最终实现对棋盘状态的有效分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个有很多细节要注意的题。。。。。交到第15发终于过了。。。。。

所有的数据都要到用longlong 然后要判断最高的棍子

将棋盘黑白染色,源向黑(1)连 1向四周0连 0向汇连

如果n*m是偶数 那么黑位置的值之和要等于白格子的值之和(因为每加一个板子,黑白的值都加一)

如果n*m是基数 那么黑棋子或白棋子多一个 d就是(sum1-sum0)/(num1-num0);

#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <queue>
#include <vector>
#include <iostream>
using namespace std;
typedef long long sint;
#define maxn 10000
#define INF (1ll<<33)
struct node
{
    int u,v;
    sint f;
}e[maxn];
int st,ed,en,first[maxn],next[maxn],cur[maxn];;
sint ans,dis[maxn],save[200][200];
int num,num0,num1,m,n,lab[200][200],lab2[100][100];
sint sum0,sum1,maxflow,lim;
int dx[]={0,1,0,0,-1};
int dy[]={0,0,1,-1,0};
sint getint()
{
    sint res,f=1;char c;
    while(c=getchar(),c<'0'||c>'9')
        if(c=='-') f=-1;
    res=c-'0';
    while(c=getchar(),c>='0'&&c<='9')
        res=res*10+c-'0';
    return res*f;
}
void init()
{
    memset(first,-1,sizeof(first));
    en=-1;
}
void add(int a,int b,sint c)
{
    en++;
    e[en].v=b;
    e[en].f=c;
    next[en]=first[a];
    first[a]=en;
    en++;
    e[en].v=a;
    e[en].f=0;
    next[en]=first[b];
    first[b]=en;
}
bool bfs()
{
    queue<int>q;
    memset(dis,-1,sizeof(dis));
    q.push(st);
    dis[st]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=first[u];i!=-1;i=next[i])
        {
            int v=e[i].v;
            if(dis[v]!=-1||e[i].f==0) continue;
            dis[v]=dis[u]+1;
            if(v==ed)
            {
                return true;
            }
            q.push(v);
        }
    }
    return false;
}
sint dfs(int x,sint mx)
{
    if(mx==0||x==ed) return mx;
    sint tmp,flow=0,res=0;
    for(int& i=cur[x];i!=-1;i=next[i])
    {
        int v=e[i].v;
        if(e[i].f==0) continue;
        if(dis[v]!=dis[x]+1) continue;
        tmp=dfs(v,min(mx,e[i].f));
        flow+=tmp;
        e[i].f-=tmp;
        e[i^1].f+=tmp;
        mx-=tmp;
        if(!mx) break;
    }
    if(!flow) dis[x]=-1;
    return flow;
}
sint dinic()
{
    sint tmp=0;
    maxflow=0;
    while(bfs())
    {
        for(int i=1;i<=ed;i++) cur[i]=first[i];
        while(tmp=dfs(st,INF)) maxflow+=tmp;
    }
    return maxflow;
}
sint getflow(sint x)
{
    int xx,yy;
    init();
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(lab2[i][j]==1)
            {
                add(st,lab[i][j],x-save[i][j]);
                for(int k=1;k<=4;k++)
                {
                    xx=dx[k]+i;
                    yy=dy[k]+j;
                    if(xx<1||xx>n||yy<1||yy>m) continue;
                    add(lab[i][j],lab[xx][yy],INF);
                }
            }
            else
            {
                add(lab[i][j],ed,x-save[i][j]);
            }
        }
    }
    return dinic();
}
bool check(sint x)
{
    init();
    sint sum=x*n*m;
    if(x<lim) return false;
    if(getflow(x)*2==sum-sum0-sum1)
        return true;
    return false;
}
void solve0()
{
    sint l=0,r=INF,mid,tmp;
    if(sum1!=sum0)
    {
        printf("-1\n");
        return;
    }
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(check(mid))
        {
            ans=maxflow;
            r=mid-1;
        }
        else
        {
            l=mid+1;
        }
    }
    if(ans!=-1)
        printf("%lld\n",ans);
    else
        printf("-1\n");
    return;
}
void solve1()
{
    sint d=(sum1-sum0)/(num1-num0);
    if(check(d))
    {
        printf("%lld\n",maxflow);
    }
    else printf("-1\n");
}
int main()
{
    //freopen("input.txt","r",stdin);
    int cas;
    scanf("%d",&cas);
    while(cas--)
    {
        scanf("%d%d",&n,&m);
        sum0=sum1=0;
        num0=num1=num=0;
        lim=-INF;
        ans=-1;
        st=m*n+1;
        ed=m*n+2;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                save[i][j]=getint();
                lim=max(lim,save[i][j]);
                num++;
                lab[i][j]=num;
                lab2[i][j]=(i+j)&1;
                if(lab2[i][j]==1)
                {
                    sum1+=save[i][j];
                    num1++;
                }
                else
                {
                    num0++;
                    sum0+=save[i][j];
                }
            }
        }
        if(n*m%2==0) solve0();
        else solve1();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值