P2774-方格取数问题【网络流,最大流,最小割】

正题

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


题意

在一个n*m的数字矩阵中取数,取得数不能相邻,求能取到的最大价值。


解题思路

最大价值,那么反着去想,就是取若干个格子,让所有格子的都不相邻,要求权最小,那么就是最小割问题。然后二分建图。起点与奇数点连容量为该点价值的边,偶数点与终点连容量为该点价值的边,然后相邻的连一条容量无限的边,这样就不会割去这条边,然后所有的权值和减去最小割就是答案。


代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
struct line{
    int to,next,c;
}a[200001];
queue<int>f;//队列
int n,d[10055],s,e,m,answ,w,tot,ls[10055];
void addl(int x,int y,int z)//加边
{
    a[++tot].to=y;a[tot].next=ls[x];a[tot].c=z;ls[x]=tot;
}
bool bfs()//在残量网上建分层图
{
    memset(d,0,sizeof(d));
    d[s]=1;f.push(s);
    while (!f.empty())
    {
        int x=f.front();
        for (int i=ls[x];i>=0;i=a[i].next)
        {
          int y=a[i].to;
          if (a[i].c>0 && d[y]==0)
          {
            d[y]=d[x]+1;
            f.push(y);
          }
        }
        f.pop();
    }
    if (d[e]) return true;
    else return false;
}
int dinic(int x,int flow)//求最大流
{
    int rest=0,k;
    if (x==e) return flow;
    for (int i=ls[x];i>=0;i=a[i].next)
    {
        int y=a[i].to;
        if (a[i].c>0&&d[y]==d[x]+1&&flow>rest)
        {
            rest+=(k=(dinic(y,min(flow-rest,a[i].c))));
            a[i].c-=k;
            a[i^1].c+=k;
        }
    }
    if (!rest) d[x]=0;
    return rest;
}
int ansq()
{
    int sum=0;
    while (bfs()) 
    sum+=dinic(s,2147483647);//统计值
    return sum;
}
int main()
{
    int x;tot=-1;
    scanf("%d%d",&n,&m);
    s=0;e=n*m+1;
    memset(ls,-1,sizeof(ls));
    for (int i=1;i<=n;i++)
      for (int j=1;j<=m;j++)
      {
        scanf("%d",&w);
        answ+=w;
        int num=(i-1)*m+j;//连边
        if ((i+j)&1) addl(s,num,w),addl(num,s,0);
        else addl(num,e,w),addl(e,num,0);
      } 
    for (int i=1;i<=n;i++)
      for (int j=1;j<=m;j++)
        if ((i+j)&1)
        {
            for (int k=0;k<4;k++)
            {
              int x1=i+dx[k],y1=j+dy[k];//相邻连边
              if (x1<=0 || x1>n || y1<=0 || y1>m) continue;
              int num=(i-1)*m+j;
              addl(num,(x1-1)*m+y1,2147483647);
              addl((x1-1)*m+y1,num,0);
            }
        }
    printf("%d",answ-ansq());//输出
}

猜你喜欢

转载自blog.csdn.net/mr_wuyongcong/article/details/80273604