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; }
重要的是自信,一旦有了自信,人就会赢得一切。