CodeVS 1907 方格取数 3 (ISAP)

来源:互联网 发布:linux echo 脚本 编辑:程序博客网 时间:2024/05/29 09:07

CodeVS1907 方格取数3

题目描述 Description
问题描述:
在一个有m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意2个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。
编程任务:
对于给定的方格棋盘,按照取数要求编程找出总和最大的数。

输入描述 Input Description
第1行有2个正整数m和n,分别表示棋盘的行数和列数。接下来的m行,每行有n个正整数,表示棋盘方格中的数。

输出描述 Output Description
将取数的最大总和输出

样例输入 Sample Input
3 3
1 2 3
3 2 3
2 3 1

样例输出 Sample Output
11

数据范围及提示 Data Size & Hint
n,m<=30

思路
要取一些数,可以相反的考虑去掉一些数。我们要保证的就是删完这些边之后,这张图上没有相邻的数,考虑网络流解题,针对这类网格图问题,间染色往往有用,不妨首先对网格进行黑白染色,然后我们就会发现,白点和白点之间,黑点和黑点之间是不会相邻的,那么如果把白点和黑点分为两个集合,集合之间相邻的点连着一些边,按照题意我们是不允许这些格子相连的,但是要使取得尽量大,那么删掉的就要尽量小,于是解法就呼之欲出了。->求出这张图的最小割。S连向所有的白点,容量等于格子中数的大小(也就是我们要删除这一个点所需要付出的代价),所有黑点连向T,容量也等于格子中的数字,然后再将白点与和它相邻的黑点连边,容量为INF。
最小割,由于我们有最大流最小割定理,所以要求的最小割就等于最大流。加入最大流失flow,所有格点的权值和为SUM,答案就是SUM-flow。
要注意的是间染色时,要保证跟上边位置不同。
在网上翻了翻题解,学习了一下ISAP,好像很短的样子。(第一次写,很辣鸡,勿喷)ISAP中是dfs一边找增广路,一遍建层次图,只不过跟dinic的距离不同,这里建立层次图用的是序号。算法还在摸索中…

#include <cstdio>#include <iostream>#include <algorithm>  using namespace std;const int N = 2000;const int inf = 0x3f3f3f3f;int n, m, ans, idc=1, S=0, T=1000, sum, flow = 0;int dist[N], last[N], num[N], head[N];  bool Exit;struct ln{    int to, next, w, last;}ed[N * 2];void adde(int u, int v, int w){    ed[++idc].to = v;    ed[idc].next = head[u];    ed[idc].w = w;    head[u] = idc;    last[u] = idc;} void init(){    int pos = 0, color = 0;      scanf("%d%d", &n, &m);      for(int i=1; i<=n; i++)          for(int j=1; j<=m; j++){              if( j == 1 ) { color = (m & 1) ? !color : color;}              else color = !color;//当M为偶数时,每一行的最后一个格子的颜色和下一行的第一个格子的颜色是相同的。            int x; scanf("%d", &x);            sum += x; ++pos;            if( color ){//黑白点                 adde(S,pos,x), adde(pos,S,0);                if(i > 1) adde(pos, pos-m, inf), adde(pos-m, pos, 0);                  if(i < n) adde(pos, pos+m, inf), adde(pos+m, pos, 0);                  if(j > 1) adde(pos, pos-1, inf), adde(pos-1, pos, 0);                  if(j < m) adde(pos, pos+1, inf), adde(pos+1, pos, 0);              }            else adde(pos, T, x), adde(T, pos, 0);        }}  int dfs(int u, int low){    if(u == T) return low;      int pp, ans = 0;      for(int k=last[u]; k; last[u]=k=ed[k].next)//之前处理过的邻接边是不需要重新处理的(残量网络中的边只会越来越少)        if(ed[k].w && dist[u] == dist[ed[k].to] + 1){            ans += pp = dfs(ed[k].to, min(ed[k].w, low-ans));              ed[k].w -= pp; ed[k^1].w += pp;              if(Exit || ans == low) return ans;          }      Exit = --num[dist[u]] == 0; num[++dist[u]]++;//如果有一层断掉了,那么一定没有增广路了。     last[u] = head[u];      return ans;  }  int main(){    init();      num[0] = n * m + 2;//相当于是最开始所有点都在T,在找增广路时建立层次图,有一种把所有点向外扩散的感觉,最终成为层次图     while( !Exit ) flow += dfs(S, inf);      printf("%d\n", sum - flow);    return 0;  }