Luogu1006 传纸条 与 Luogu P2045方格取数加强版 (费用流)

Posted gzygzy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu1006 传纸条 与 Luogu P2045方格取数加强版 (费用流)相关的知识,希望对你有一定的参考价值。

Luogu1006 传纸条 与 Luogu P2045方格取数加强版

其实就是这几道题
在一个有m*n 个方格的棋盘中
每个方格中有一个正整数
现要从在方格中从左上角到右下角取数,只能向右或向下走
每走到一个格子就可以把这个位置上的数取走(下次经过就没有了)
1.让你走1次,求取出的数的总和最大是多少
2.让你走2次,求取出的数的总和最大是多少
3.让你走k次,求取出的数的总和最大是多少

对于第一问,十分显然.
(f[i][j])表示(i)(j)列的最大价值,转移即可。
第二问,较上一题有些难度。
考虑有性质:
1,如果两人相遇,那么一定不是最优的
2.走n+m步数.
用性质1:设状态(f[i][j][k][l])表示第一个人走到i,j第二个人走到k,l两人不相交的最大价值.
转移的时候只要判断一下路径是否相交就ok了。
没有完全利用好性质
用性质2:设状态(f[k][i][j])表示第一个人和第二个人都走了k步,第一个人到了((i,k?j)),第二个人到了((j,k?j))的最大价值.
也就是传纸条
第三问
考虑费用流。
开一个源点连向((1,1)),流量为(k),费用为(0)
对于一个点,先拆点。
一条是流量为(inf),费用为(0),另外一条是流量(inf),费用为(a)
然后向下和右连边.

/*header*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <map>
#include <queue>
#define gc getchar()
#define pc putchar
#define ll long long
#define mk make_pair
#define fi first
#define se second
using std::min;
using std::max;
using std::swap;
const int inf = 0x3f3f3f3f;
 
inline int gi() {
  int x = 0,f = 1;char c = gc;
  while(c < '0' || c > '9') {if(c == '-')f = -1;c = gc;}
  while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = gc;}return x * f;
}
 
const int maxN = 5000 + 7;
const int maxM = 100000 + 7 ;
 
using namespace std;
int n, m, s, t, ans, maxflow, k;
 
int head[maxN];
struct Node{
    int u,v,flow,spend,nex;
}Map[maxM];
 
int dis[maxN],vis[maxN],num,path[maxN];
 
void init() {
    s = n * n * 2 + 1;
    t = s + 1;
  num = -1;
  memset(head,-1,sizeof(head));
  return;
}
 
void add_Node(int u,int v,int w,int spend) {
  Map[++ num] = (Node) {u , v, w, spend, head[u]};head[u] = num;
  Map[++ num] = (Node) {v , u, 0, -spend, head[v]};head[v] = num;
  return ;
}
 
bool spfa() {
  queue<int>q;
  q.push(s);
  memset(dis,0x3f,sizeof(dis));
  memset(path,0,sizeof(path));
  dis[s] = 0;
  vis[s] = true;
  while(!q.empty()) {
    int p = q.front();q.pop();
    vis[p] = false;
        for(int i = head[p];i != -1;i = Map[i].nex) {
        int v = Map[i].v;
        if(dis[v] > dis[p] + Map[i].spend && Map[i].flow) {
          dis[v] = dis[p] + Map[i].spend;
          path[v] = i;
          if(!vis[v]) {
            q.push(v);
            vis[v] = true;
            }
        }
      }
  }
  if(dis[t] == 0x3f3f3f3f) return false;
  return true;
}
int min(int a,int b) {return a > b ? b : a ;} 
 
void f() {
  int mn = 0x7fffffff;
  for(int i = t;i != s;i = Map[path[i]].u) 
    mn = min(mn,Map[path[i]].flow);
  ans += mn;
  for(int i = t;i != s;i = Map[path[i]].u) {
    Map[path[i]].flow -= mn;
    Map[path[i] ^ 1].flow += mn;
    maxflow += mn * Map[path[i]].spend;
    }
}
 
void EK() {
  while(spfa()) 
    f();
  printf("%d",-maxflow);
  return ;
}
 
int a[57][57];
 
int main() {
  n = gi();int k = gi();
  init();
  for(int i = 1;i <= n;++ i) for(int j = 1;j <= n;++ j) a[i][j] = gi();
  for(int i = 1;i <= n;++ i) for(int j = 1;j <= n;++ j) add_Node((i - 1) * n + j, (i - 1) * n + j + n * n, inf, 0) , add_Node((i - 1) * n + j, (i - 1) * n + j + n * n, 1, - a[i][j]);
    add_Node(s , 1, k, 0);
    for(int i = 1;i <= n;++ i)  {
        for(int j = 1;j <= n;++ j) {
            if(i < n) add_Node((i - 1) * n + j + n * n , i * n + j, inf, 0);
            if(j < n) add_Node((i - 1) * n + j + n * n , (i - 1) * n + j + 1, inf, 0);
        }   
    }
    add_Node(2 * n * n , t, k, 0);
  EK();
  return 0;
}

以上是关于Luogu1006 传纸条 与 Luogu P2045方格取数加强版 (费用流)的主要内容,如果未能解决你的问题,请参考以下文章

Luogu 1006 传纸条

luogu 1006 传纸条

[Luogu P1006]传纸条 (网格DP)

luogu 1006 noip2008 传纸条

洛谷 P1006 传纸条 题解

cogs luogu 传纸条 2008年NOIP全国联赛提高组 WD