【codevs1907】方格取数3(最大流最小割定理)

  网址:http://codevs.cn/problem/1907/

  题意:在一个矩阵里选不相邻的若干个数,使这些数的和最大。

  我们可以把它看成一个最小割,答案就是矩阵中的所有数-最小割。先把矩阵按国际象棋棋盘黑白染色(即把相邻的点分别染成白色和黑色),然后黑点连源点,白点连汇点。割掉一个点到源/汇的边就是不选择这个点,最后的目的就是使源到汇不连通(不引发题目不能选择位置相邻的数的矛盾)。

  然而最小割怎么求呢?

  于是我们就要引入一个定理:最大流最小割定理。它的意思就是说,在一个图中,a点到b点的最小割=a到b的最大流。

  然而我并不会证……这里口胡一个想法:最大流就是沿着剩余网络不断地流,每流一次相当于删掉剩余网络的一条边,流到不能流为止。而最小割也是不断地割直到不连通。于是最小割=最大流。

  答案就这样变成了求最大流。

  具体怎么建图,就是把黑/白点到源/汇的边的流量定为这个位置的上数,而黑白点之间的边,因为不能把它割掉,所以把它的流量设为一个极大数。

  于是就过了。

  代码:

var a:array[0..1010,0..1010]of longint;
  s:array[0..50,0..50]of longint;
  l,q:array[0..1010]of longint;
  n,m,nn,i,j,k,h,t:longint;
  ans,sum:int64;
function num(x,y:longint):longint;
begin
  exit((x-1)*m+y);
end;
function dfs(now,ll:longint):longint;
var p,i:longint;
begin
  if now=nn then exit(ll);
  for i:=1 to nn do
    if(a[now,i]>0)and(l[i]=l[now]+1)then begin
      if a[now,i]<ll then p:=dfs(i,a[now,i])
      else p:=dfs(i,ll);
      a[now,i]:=a[now,i]-p; a[i,now]:=a[i,now]+p;
      if p>0 then exit(p);
    end;
  exit(0);
end;
begin
  read(n,m); sum:=0;
  for i:=1 to n do
    for j:=1 to m do begin
      read(s[i,j]);
      sum:=sum+s[i,j];
    end;
  fillchar(a,sizeof(a),0);
  for i:=1 to n do
    for j:=1 to m do
      if (i+j)and 1=0 then a[num(i,j),n*m+1]:=s[i,j]
      else begin
        if i>1 then a[num(i,j),num(i-1,j)]:=1<<25;
        if i<n then a[num(i,j),num(i+1,j)]:=1<<25;
        if j>1 then a[num(i,j),num(i,j-1)]:=1<<25;
        if j<m then a[num(i,j),num(i,j+1)]:=1<<25;
        a[0,num(i,j)]:=s[i,j];
      end;
  nn:=n*m+1; ans:=0;
  while true do begin
    fillchar(l,sizeof(l),0);
    h:=1; t:=1; q[1]:=0; l[0]:=1;
    repeat
      for i:=1 to nn do
        if(a[q[h],i]>0)and(l[i]=0)then begin
          inc(t); q[t]:=i; l[i]:=l[q[h]]+1;
        end;
      inc(h);
    until h>t;
    if l[nn]=0 then break;
    repeat
      k:=dfs(0,1<<25);
      ans:=ans+k;
    until k=0;
  end;
  writeln(sum-ans);
end.

codevs1907方格取数

时间: 2024-08-02 02:44:11

【codevs1907】方格取数3(最大流最小割定理)的相关文章

LuoguP2774 方格取数问题(最小割)

题目背景 none! 题目描述 在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数.现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大.试设计一个满足要求的取数算法.对于给定的方格棋盘,按照取数要求编程找出总和最大的数. 输入输出格式 输入格式: 第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数.接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数. 输出格式: 程序运行结束时,将取数的最大总和输出 解题思路: 想个办法将选一个点和不选周围点关

方格取数问题 最小割

题目背景 none! 题目描述 在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数.现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大.试设计一个满足要求的取数算法.对于给定的方格棋盘,按照取数要求编程找出总和最大的数. 输入输出格式 输入格式: 第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数.接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数. 输出格式: 程序运行结束时,将取数的最大总和输出 输入输出样例 输入样例#1: 复制 3 3

【luogu 2774】方格取数 (最小割)

题目链接 [题目大意] 有n*m的方格,在其中取任意个格子的数,保证最终结果最大,取得数字不能相邻 [题目思路] 如果不是知道是网络流的题,大概会试下爆搜,取之后周围的不可取之类的,但是显而易见会T 然后考虑怎么用网络流做,为什么能用网络流做 我自己对于网络流的理解,是解决选择之间的直接冲突,从而得到最优解的方法,而这个题每一个数字选和不选之间就会发生冲突 [建图] 如何实现选择 拆点,这里大多数程序都是将点根据(i+j)分成两类,我觉得是为了代码更简洁些,如果把所有点都拆成两个的话还是可行的

codevs1907 方格取数 3

嗯......这道题大概算是自己想出来的第一道网络流的题吧? 虽然想了很久,WA了很多发,但终于A掉了...... 网络流的题真是难想,如果不是我已经知道这道题要用网络流做,还不知道要想到什么时候去了...... 好了,不扯多了,进正题: 首先,我们发现直接建模的话非常不好搞,体重的条件不好表示...... 于是,我们就想,是否可以把我们选完书之后剩下的数给表示出来呢?我们发现这个不难做到.只需将棋盘黑白二染色,把黑点.白点各看成一块,相邻的格子间有边相连,不难发现将黑白两块分开的割的方案就是不

HDU 1569 方格取数(2) (最小割)

题意:中文题. 析:很明显的是二分图的最大独立集,但是每个点都有权值,这个可以用最小割来求,建立一个超级源点s,和汇点t,然后s 向 X集,添加容量为权值的边,Y集向 t 添加容量为权值的,然后跑一遍最小割,然后用总权值减去就是答案了. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #include <cst

线性规划与网络流24题●09方格取数问题&amp;13星际转移问题

●(做codevs1908时,发现测试数据也涵盖了1907,想要一并做了,但因为"技术"不佳,搞了一上午) ●09方格取数问题(codevs1907  方格取数3) 想了半天,也没成功建好图: 无奈下参考题解,说是本题要求二分图点权最大独立集,然后可以由结论:"最大点权独立集 = 所有点权 - 最小点权覆盖集 = 所有点权 - 最小割集 = 所有点权 - 网络最大流"转化到求最大流(我真的很懵逼,但又感觉很有道理): 下面附上solution:(自己领悟吧) (不懂

二分图最小点权覆盖 二分图最大权独立集 方格取数 最小割

二分图最小点权覆盖: 每一条边 (u, v) 都是一个限制条件, 要求 u 和 v 不能同时取得. 我们考虑先取得所有的, 然后减去最小的点权. 建立原点 S , 连向二分图左边的所有点, 与 S 连通的意义是左边的点被选择了, 或者右边的点没有被选择. 建立汇点 T , 二分图右边的所有点连向它, 与 T 连通的意义是左边的点没有被选择, 或者右边的点被选择了. 利用最小割最大流定理, 我们跑最大流, 再根据最后一次 BFS 得出的情报构造方案. 定理 覆盖集与独立集互补. 证明 即证明覆盖集

hdoj 1569 方格取数(2) 【最小割】 【最大点权独立集】

方格取数(2) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 5589    Accepted Submission(s): 1741 Problem Description 给你一个m*n的格子的棋盘,每个格子里面有一个非负数. 从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的

hdu 3657 最小割的活用 / 奇偶方格取数类经典题 /最小割

题意:方格取数,如果取了相邻的数,那么要付出一定代价.(代价为2*(X&Y))(开始用费用流,敲升级版3820,跪...) 建图:  对于相邻问题,经典方法:奇偶建立二分图.对于相邻两点连边2*(X&Y),源->X连边,Y->汇连边,权值w为点权. ans=总点权-最小割:如果割边是源->X,表示x不要选(是割边,必然价值在路径上最小),若割边是Y-汇点,同理:若割边是X->Y,则表示选Y点且选X点, 割为w( 2*(X&Y) ). 自己的确还没有理解其本质