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;
}

原文地址:https://www.cnblogs.com/gzygzy/p/10357935.html

时间: 2024-10-15 04:06:53

Luogu1006 传纸条 与 Luogu P2045方格取数加强版 (费用流)的相关文章

Luogu P2045 方格取数加强版 题解

闲扯 所以我还是不会做网络流啊... 打个模板多轻松啊,为什么还要建图呢,天空这么蓝,森林那么绿,这个世界多么美好啊! 建图的套路感觉好多啊..还是慢慢学吧.. Solution 题目分析/建图 因为限制了方向,同时还限制了每一个最多取一次,要求和最大,想到了什么?什么都没想到 最大费用最大流! 因为每个点只能选一次,所以我们考虑把这个点拆开,变成一个入点,一个出点,然后在入点和出点之间连上一条流量为 \(1\) ,费用为 \(val_i\) 的边.但是每一个的数取了后还是可以经过这个位置的,所

P2045 方格取数加强版

题目描述 给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大 输入输出格式 输入格式: 第一行两个数n,k(1<=n<=50, 0<=k<=10) 接下来n行,每行n个数,分别表示矩阵的每个格子的数 输出格式: 一个数,为最大和 输入输出样例 输入样例#1: 3 1 1 2 3 0 2

【Luogu】P2045方格取数加强版(最小费用最大流)

题目链接 通过这题我学会了引诱算法的行为,就是你通过适当的状态设计,引诱算法按照你想要它做的去行动,进而达到解题的目的. 最小费用最大流,首先将点拆点,入点和出点连一条费用=-权值,容量=1的边,再连费用=0,容量=INF的边,跑最小费用最大流即可. #include<cstdio> #include<cctype> #include<algorithm> #include<cstring> #include<cstdlib> #include&

[洛谷P2045]方格取数加强版

题目大意:有一个n*n的矩阵,每个格子有一个非负整数,规定一个人从(1,1)开始,只能往右或下走,走到(n,n)为止,并把沿途的数取走,取走后数变为0.这个人共取n次,求取得的数的最大总和. 解题思路:由于取多少次不确定,所以不能用dp. 我们发现,一个格子只能从左边或上面走来,且数只能取到一次,那么我们可以把此题转化为最大费用最大流问题.首先拆点,将一个点拆成x和y,然后从x到y连一条容量为1,流量为x(x为这格的数)的边,然后再连一条容量为inf,费用为0的边,这样即可保证一个点可以走多次,

poj 3422 洛谷P2045 K取方格数(方格取数加强版)

Description: 给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大 Input: 第一行两个数n,k(1<=n<=50, 0<=k<=10) 接下来n行,每行n个数,分别表示矩阵的每个格子的数 Output: 一个数,为最大和 思路:仍旧是拆点 因为每个点都有一个限制K和一个价

[Luogu2045] 方格取数加强版

题目描述 给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大 输入输出格式 输入格式: 第一行两个数n,k(1<=n<=50, 0<=k<=10) 接下来n行,每行n个数,分别表示矩阵的每个格子的数 输出格式: 一个数,为最大和 输入输出样例 输入样例#1: 复制 3 1 1 2 3 0

luogu P1004 方格取数

题目描述 设有N*N的方格图(N<=9),我们将其中的某些方格中填入正整数,而其他的方格中则放 人数字0.如下图所示(见样例): A 0 0 0 0 0 0 0 0 0 0 13 0 0 6 0 0 0 0 0 0 7 0 0 0 0 0 0 14 0 0 0 0 0 21 0 0 0 4 0 0 0 0 15 0 0 0 0 0 0 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . B 某人从图的左上角的A点出发,可以向下行走,也可以向右走,直到到达右下角的B 点.在走过的路上

luogu P2774 方格取数问题

有限制的问题,显然考虑全选再根据限制去掉的想法较优,我们发现一个点四周的点受限,其x或者y差一,也就是说奇偶性不同,那我们可以将其分成白点和黑点,就变成了最小割的问题,将每个白点向受限制的黑点连边,capacity为INF,每个黑点向汇点连边,capacity为该点的值,同理,源点向每个白点连边,这样受限的每一组之间都只会选出一个最小的来,通过capacity的限制来实现,最大流=最小割,将总和减去最小割(每一组最小的)就是答案 每一组黑白点,capacity来限制最小权,转换求最小割 #inc

[luoguP2045] 方格取数加强版(最小费用最大流)

传送门 水题 ——代码 1 #include <queue> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #define N 51 6 #define M 100001 7 #define INF 1e9 8 #define min(x, y) ((x) < (y) ? (x) : (y)) 9 10 int n, k, s, t, cnt, tot, sum; 11