1. 程式人生 > >Luogu1006 傳紙條 與 Luogu P2045方格取數加強版 (費用流)

Luogu1006 傳紙條 與 Luogu P2045方格取數加強版 (費用流)

min cto cpp ++ 是否 putc iostream stream std

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方格取數加強版 (費用流)