Wpremig和Jhadgre的藏宝图(最大流+二分)

Wpremig和Jhadgre的藏宝图

题目链接:https://ac.nowcoder.com/acm/contest/333/M

Description:

Jhadgre在生日那天收到了一张神秘的藏宝图,于是他决定和Wpremig分享这张藏宝图,并且去寻宝!

这张藏宝图上总共有N行,每行有M个格子,共N*M个格子,每个格子上写了一个数字。

藏宝图上写了一段字:神秘的有缘人啊,这些格子已经被其他人挖过了,我已经替你标出了所有格子被挖到的深度,并且在这个宝藏上加了一个魔法,只要你把所有格子都挖到同一个深度,宝藏自然就会出现。

这看似是一个很简单的问题,只要知道现在最深的那个格子,其他格子都挖到这个深度,就是最快的方式了。然而Wpremig和Jhadgre却发现,还有一句话:我知道你们有两个人,所以你们在挖的时候必须两个人同时各挖一个格子,并且这两个格子必须相邻!

这就比较尴尬了,Wpremig和Jhadgre很不高兴,这是送礼呢还是膈应人呢?不挖了!

虽然这样说,但是人总是会遵循“真香定理”。在几天后Wpremig和Jhadgre还是决定去挖宝藏,现在他们在出发前想知道,最少需要挖多少次才能获得宝藏呢?(假设每次一个人挖一个格子可以将这个格子的深度+1)

Input:

第一行一个整数T表示数据组数(T<=10)
对于每组数据,第一行两个整数N,M。
接下去N行每行M个整数。
保证 T<=10,1<=N,M<=40,所有数为正整数且小于1000000000

Output:

对于每组数据输出最少的挖掘次数,若无论如何都不能获得宝藏,则输出-1;

Sample Input:

1
2 2 
1 2 
2 3

Sample Output:

2

 

题解:

这里挖格子必须两个相邻的格子一起挖,我们发现二分图也是相邻的格子在两边,那么我们就可以首先将图进行黑白染色,之后令黑在左,白在右进行构图。

每一个黑格子肯定连向与它相邻的白格子,现在来确定容量。

我们假定最后的深度为x,设黑色格子数量为black,白色格子数量为white,黑色格子目前的数量和为sumblack,白色格子的数量和为sumwhile,那么最后的状态就有:

black*x-sumblack = white*x-sumwhite,最后有(black-white)*x = sumblack-sumwhite。

当black=white时,首先要有两个sum相等,然后就可以check求出x最优值;如果black!=white,我们可以将x接出来,然后checkx看是否合法。

对于check就是边容量的问题,这里的x是最大深度,那么我们与源点以及汇点连接的边权就为x减去目前深度就好了。

我一开始就是想直接checkx,但是发现黑白格子数量会影响到x的取值= =

 

代码如下:

#include <bits/stdc++.h>
#define s 0
#define t 1700
#define INF 1e18
using namespace std;
typedef long long ll;
const int N = 45,M = 45;
int T,n,m,tot,mx;
ll sum1,sum2;
ll head[1705],d[1705],cnt[4];
int a[N][M],color[N][M];
int dx[5]={1,0,0,-1};
int dy[5]={0,1,-1,0};
struct Edge{
    ll c;
    int v,next;
}e[10010];
void adde(int u,int v,ll c){
    e[tot].v=v;e[tot].next=head[u];e[tot].c=c;head[u]=tot++;
    e[tot].v=u;e[tot].next=head[v];e[tot].c=0;head[v]=tot++;
}
bool ok(int x,int y){
    return x>=1 && x<=n && y>=1 && y<=m;
}
bool bfs(int S,int T){
    memset(d,0,sizeof(d));d[S]=1;
    queue <ll > q;q.push(S);
    while(!q.empty()){
        ll u=q.front();q.pop();
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(!d[v] && e[i].c>0){
                d[v]=d[u]+1;
                q.push(v);
            }
        }
    }
    return d[T]!=0;
}
ll dfs(int S,ll A){
    ll flow=0,f;
    if(S==t || A==0) return A;
    for(ll i=head[S];i!=-1;i=e[i].next){
        ll v=e[i].v;
        if(d[v]!=d[S]+1) continue ;
        f=dfs(v,min(A,e[i].c));
        if(f){
            e[i].c-=f;
            e[i^1].c+=f;
            flow+=f;
            A-=f;
            if(A==0) break;
        }
    }
    if(!flow) d[S]=-1;
    return flow;
}
ll Dinic(){
    ll max_flow=0;
    while(bfs(0,t)){
        max_flow+=dfs(0,INF);
    }
    return max_flow;
}
void build(ll x){
    tot=0;memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(color[i][j]==1) adde(s,(i-1)*m+j,x-a[i][j]);
            else adde((i-1)*m+j,t,x-a[i][j]);
            if(color[i][j]==2) continue ;
            for(int k=0;k<4;k++){
                int nowx = i+dx[k],nowy = j+dy[k];
                if(ok(nowx,nowy)) adde((i-1)*m+j,(nowx-1)*m+nowy,INF);
            }
        }
    }
}
void dfs_col(int x,int y,int col){
    color[x][y]=col;
    cnt[col]++;
    if(col==1) sum1+=a[x][y];
    if(col==2) sum2+=a[x][y];
    for(int i=0;i<4;i++){
        ll nowx = x+dx[i],nowy = y+dy[i];
        if(ok(nowx,nowy)&&!color[nowx][nowy]) dfs_col(nowx,nowy,3-col);
    }
}
int check(ll x){
    build(x);
    ll flow = Dinic();
    for(int i=head[s];i!=-1;i=e[i].next) if(e[i].c) return 0;
    return 1;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        mx = 0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&a[i][j]);
                mx=max(a[i][j],mx);
            }
        }
        memset(color,0,sizeof(color));
        memset(cnt,0,sizeof(cnt));
        sum1 = sum2 = 0;
        dfs_col(1,1,1);
        if((n*m)%2==0){
            if(sum1!=sum2){
                printf("-1\n");
                continue ;
            }
            ll l=mx,r=1e13,mid;
            while(l<r){
                mid=l+r>>1;
                if(check(mid)) r=mid;
                else l=mid+1;
            }
            if(l==1e13) printf("-1\n");
            else printf("%lld\n",(l*m*n-sum1-sum2)/2);
        }else{
            ll x = (sum1-sum2)/(cnt[1]-cnt[2]);
            if(check(x)) printf("%lld\n",(x*m*n-sum1-sum2)/2);
            else printf("-1\n");;
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/heyuhhh/p/10230058.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值