【LOJ】6007 「网络流 24 题」方格取数 网络流

来源:互联网 发布:skype for mac 编辑:程序博客网 时间:2024/06/04 20:06

题目传送门

这题的解题思路和“【BZOJ】1497 [NOI2006]最大获利”这题差不多。

因为如果一个方格被选取,则与这个方格四联通的方格都不能被选取,那么我们可以对整个棋盘染黑白两色并建边:

  1. 所有黑色方格与源点建一条流量为方格中数字的边。
  2. 所有白色方格与汇点建立一条流量为方格中数字的边。
  3. 所有黑色的方格与其四联通的白色方格建立一条流量为+的边。

然后对新建出来的图做一遍最小割,也就是最大流,割源点与黑色方格的边表示放弃黑色方格内的数,割白色方格与汇点的边表示放弃白色方格内的数,最后棋盘内所有数的和减去最大流即为答案。

附上AC代码:

#include <cstdio>#include <queue>#include <cstring>using namespace std;const int N=1000,dx[5]={0,1,0,-1,0},dy[5]={0,0,1,0,-1};struct side{    int to,w,nt;}s[5000];int n,m,x,st,ed,map[31][31],num,h[N],ans;queue <int> que;int d[N],cur[N];inline void add(int x,int y,int w){    s[num]=(side){y,w,h[x]},h[x]=num++;    s[num]=(side){x,0,h[y]},h[y]=num++;}inline bool bfs(int st,int ed){    while (!que.empty()) que.pop();    memset(d,0,sizeof d),d[st]=1,que.push(st);    while (!que.empty()){        int p=que.front();que.pop();        for (int i=h[p]; ~i; i=s[i].nt)            if (s[i].w&&!d[s[i].to]) d[s[i].to]=d[p]+1,que.push(s[i].to);        if (d[ed]) return 1;    }    return 0;}inline int so(int x,int w){    if (x==ed) return w;    int sum=0,f;    for (int &i=cur[x]; ~i; i=s[i].nt)        if (s[i].w&&d[s[i].to]==d[x]+1&&(f=so(s[i].to,min(w-sum,s[i].w)))){            s[i].w-=f,s[i^1].w+=f,sum+=f;            if (sum==w) return sum;        }    if (!sum) d[x]=0;    return sum;}#define wz(x,y) ((x-1)*m+y)int main(void){    scanf("%d%d",&n,&m),st=0,ed=wz(n,m)+1,memset(h,-1,sizeof h);    for (int i=1; i<=n; ++i)        for (int j=1; j<=m; ++j){            scanf("%d",&x),ans+=x;            if ((i+j)%2==0){                add(st,wz(i,j),x);                for (int k=1; k<=4; ++k){                    int fx=i+dx[k],fy=j+dy[k];                    if (fx<1||fx>n||fy<1||fy>m) continue;                    add(wz(i,j),wz(fx,fy),2e9);                }            }            else add(wz(i,j),ed,x);        }    while (bfs(st,ed)) memcpy(cur,h,sizeof h),ans-=so(st,0x7fffffff);    return printf("%d\n",ans),0;}
原创粉丝点击