BZOJ1324Exca王者之剑&BZOJ1475方格取数——二分图最大独立集
题目描述
输入
第一行给出数字N,M代表行列数.N,M均小于等于100 下面N行M列用于描述数字矩阵
输出
输出最多可以拿到多少块宝石
样例输入
2 2
1 2
2 1
1 2
2 1
样例输出
4
题意就是选取一些点使他们互不相邻且使选取点的点权和最大。我们将网格图黑白染色,将相邻点连边,显然这是个二分图,我们要求的就是二分图的最大独立集。建模时将源点连向黑点,流量为点权;黑点连向与它相邻的白点,流量为$INF$;将白点连向汇点,流量为点权。答案就是总点权和$-$最小割,被割的边所连点就是不选取的点。因为最小割使源汇点不连通,所以所有选取的黑点都不会流向白点,所有选取的白点不会有黑点流过来,即相邻的点不会被同时选取。
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cmath> using namespace std; int head[12000]; int next[50000]; int to[50000]; int val[50000]; int d[12000]; int q[12000]; int n,m; int tot=1; int ans=0; int S,T; int s[200][200]; void add(int x,int y,int v) { tot++; next[tot]=head[x]; head[x]=tot; to[tot]=y; val[tot]=v; tot++; next[tot]=head[y]; head[y]=tot; to[tot]=x; val[tot]=0; } bool bfs(int S,int T) { int r=0; int l=0; memset(q,0,sizeof(q)); memset(d,-1,sizeof(d)); q[r++]=S; d[S]=0; while(l<r) { int now=q[l]; for(int i=head[now];i;i=next[i]) { if(d[to[i]]==-1&&val[i]!=0) { d[to[i]]=d[now]+1; q[r++]=to[i]; } } l++; } return d[T]!=-1; } int dfs(int x,int flow) { if(x==T) { return flow; } int now_flow; int used=0; for(int i=head[x];i;i=next[i]) { if(d[to[i]]==d[x]+1&&val[i]!=0) { now_flow=dfs(to[i],min(flow-used,val[i])); val[i]-=now_flow; val[i^1]+=now_flow; used+=now_flow; if(now_flow==flow) { return flow; } } } if(used==0) { d[x]=-1; } return used; } void dinic() { while(bfs(S,T)==true) { ans-=dfs(S,0x3f3f3f); } } int main() { scanf("%d%d",&m,&n); S=n*m+1; T=n*m+2; for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { scanf("%d",&s[i][j]); ans+=s[i][j]; if((i+j)%2==0) { add(S,n*(i-1)+j,s[i][j]); } else { add(n*(i-1)+j,T,s[i][j]); } } } for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { if((i+j)%2==0) { if(i-1>0) { add(n*(i-1)+j,n*(i-2)+j,1<<30); } if(j-1>0) { add(n*(i-1)+j,n*(i-1)+j-1,1<<30); } if(i+1<=m) { add(n*(i-1)+j,n*i+j,1<<30); } if(j+1<=n) { add(n*(i-1)+j,n*(i-1)+j+1,1<<30); } } } } dinic(); printf("%d",ans); }