【网络流】 hdu 1569 方格取数(2)

题意:给你一个m*n的格子的棋盘,每个格子里面有一个非负数。从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。
难度:1
题解:最大点权独立集。与骑士共存问题相似,用最大流求解。对每个点进行黑白染色,使得每对相邻的点的颜色不同,新建一个附加源s和一个附加汇t,s对所有黑点连一条容量为改点权值的边,所有白点对t连一条容量为改点权值的边,所有黑点对其相邻的点连一条容量无穷大的边,所有点的权值和减去求得的最大流就是答案。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define inf (1<<29)
#define forn(i,n) for(i=0;i<(n);i++)
#define for1(i,n) for(i=1;i<=(n);i++)
#define forh(i,u) for(i=head[u];i!=-1;i=edge[i].next)
const int maxn = 5005 , maxm = 2500005;
struct Edge {
     int u,v,c,next;
}edge[maxm];
int E,head[maxn];
int dep[maxn];
int gap[maxn];
int n;
void init() {
     E=0;memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int c) {
     edge[E].u=u;
     edge[E].v=v;
     edge[E].c=c;
     edge[E].next=head[u];
     head[u]=E++;
     edge[E].u=v;
     edge[E].v=u;
     edge[E].c=0;
     edge[E].next=head[v];
     head[v]=E++;
}
void bfs(int scr,int des) {
     memset(dep,-1,sizeof(dep));
     memset(gap,0,sizeof(gap));
     gap[0]=-1;
     int que[maxn];
     int front,rear;
     front=rear=0;
     dep[des]=0;
     que[rear++]=des;
     while(front!=rear) {
          int u=que[front++];
          if(front==maxn) front=0;
          for(int i=head[u];i!=-1;i=edge[i].next) {
                int v=edge[i].v;
                if(edge[i].c!=0||dep[v]!=-1) continue;
                que[rear++]=v;
                if(rear==maxn) rear=0;
                dep[v]=dep[u]+1;
                ++gap[dep[v]];
          }
     }
}
int sap(int scr,int des) {
    int res = 0;
    bfs(scr,des);
    int cur[maxn];
    int S[maxn];
    int top = 0;
    memcpy(cur,head,sizeof(head));
    int u=scr;
    int i;
    while(dep[scr]<n) {
         if(u==des) {
              int tmp = inf;
              int inser;
              for(i=0;i<top;i++)
                   if(tmp>edge[S[i]].c)
                        tmp=edge[S[inser=i]].c;
              for(i=0;i<top;i++) {
                   edge[S[i]].c -= tmp;
                   edge[S[i]^1].c += tmp;
              }
              res += tmp;
              top = inser;
              u = edge[S[top]].u;
         }
         if(u!=des&&gap[dep[u]-1]==0) break;
         for(i=cur[u];i!=-1;i=edge[i].next)
              if(edge[i].c!=0&&dep[u]==dep[edge[i].v]+1)
                   break;
         if(i!=-1) {
              cur[u] = i;
              S[top++] = i;
              u = edge[i].v;

         }
         else {
              int Min = n;
              for(i=head[u];i!=-1;i=edge[i].next) {
                   if(edge[i].c==0) continue;
                   if(Min>dep[edge[i].v]) {
                        Min = dep[edge[i].v];
                        cur[u] = i;
                   }
              }
              --gap[dep[u]];
              dep[u]=Min+1;
              ++gap[dep[u]];
              if(u!=scr)
                   u=edge[S[--top]].u;
         }
    }
    return res;
}
int g[55][55];
int m;
int main() {
    while(~scanf("%d%d",&n,&m)) {
        init();
        int tot = 0 , i , j;
        for1(i,n) for1(j,m) { scanf("%d",&g[i][j]); tot += g[i][j]; }
        int s = 0 , t = n * m + 1;
        for1(i,n) for1(j,m) {
            int u = (i-1) * m + j;
            if((i+j) % 2) {
                addedge(s,u,g[i][j]);
                if(i<n) addedge(u,u+m,inf);
                if(i>1) addedge(u,u-m,inf);
                if(j<m) addedge(u,u+1,inf);
                if(j>1) addedge(u,u-1,inf);
            }
            else addedge(u,t,g[i][j]);
        }
        n = n*m+2;
        int ans = sap(s,t);
        printf("%d\n",tot - ans);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值