【网络流24题】方格取数游戏

链接

https://www.luogu.org/problemnew/show/P2774

大意

给定一个 n × m 矩阵,先要取出若干个元素使得剩下的元素周围都没有元素,求求出的元素的最大和

思路

最大流求最小割
给定样例

3 2
1 3
5 2
4 6

其编号为

1 2
3 4
5 6

我们把这 n × m 个点分成两部分,变成一张二分图,然后给其建立权值(为自己本身),如下图
1
然后,对周围的点两两连边,容量为无穷大,但不能比 S 的容量大
2
再跑一遍网络流即可

代码

#include<cstring>
#include<cstdio>
#include<queue>
#define N 10001
#define M 80001
#define k(i,j) (i-1)*m+j
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b
using namespace std;int id,f,n,m,ans,d[N],l[M],s,t,sum,a;char c;
int read()
{
    char c;f=0;
    while(c=getchar(),c<=47||c>=58);f=(f<<3)+(f<<1)+c-48;
    while(c=getchar(),c>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
    return f;
}
struct node{int next,to,w;}e[M];int tot;
const short dx[4]={-1,0,1,0};
const short dy[4]={0,1,0,-1};
void add(int u,int v,int w)
{
    e[tot].to=v;e[tot].w=w;e[tot].next=l[u];l[u]=tot++;
    e[tot].to=u;e[tot].w=0;e[tot].next=l[v];l[v]=tot++;
    return;
}
bool bfs()
{
    memset(d,-1,sizeof(d));
    queue<int>q;
    q.push(s);d[s]=0;
    while(q.size())
    {
        int x=q.front();q.pop();
        for(int i=l[x];i!=-1;i=e[i].next)
        {
            int y=e[i].to;
            if(e[i].w&&d[y]==-1)
            {
                d[y]=d[x]+1;
                q.push(y);
                if(y==t) return true;
            }
        }
    }
    return false;
}
int dfs(int x,int flow)
{
    if(x==t||!flow) return flow;
    int rest=0,f;
    for(int i=l[x];i!=-1;i=e[i].next)
    {
        int y=e[i].to;
        if(d[x]+1==d[y]&&e[i].w)
        {
            f=dfs(y,min(flow-rest,e[i].w));
            e[i].w-=f;rest+=f;e[i^1].w+=f;
            if(flow==rest) return rest;
        }
    }
    if(!rest) d[x]=-1;
    return rest;
}
int dinic()
{
    int r=0;
    while(bfs()) r+=dfs(s,1e9);
    return r;
}
int main()
{
    memset(l,-1,sizeof(l));
    n=read();m=read();s=n*m+1;t=s+1;
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      {
        sum+=(a=read());//总量
        id=k(i,j);
        if((i+j)&1) add(s,id,a);else add(id,t,a);//与源点或者汇点建图
      }
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      if((i+j)&1)
       for(int o=0;o<4;o++)
       {
        int nx=i+dx[o],ny=j+dy[o];
        if(nx<1||ny<1||nx>n||ny>m) continue;
        id=k(nx,ny);
        add(k(i,j),id,1e8);//和周围的点建图
       }
    printf("%d",sum-dinic());//输出
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/80758264