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

  题目链接

  通过这题我学会了引诱算法的行为,就是你通过适当的状态设计,引诱算法按照你想要它做的去行动,进而达到解题的目的。

  最小费用最大流,首先将点拆点,入点和出点连一条费用=-权值,容量=1的边,再连费用=0,容量=INF的边,跑最小费用最大流即可。

  

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<queue>
#define maxn 10000
#define maxm 500000
using namespace std;

inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch==‘-‘)    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-‘0‘;
        ch=getchar();
    }
    return num*f;
}

int count(int i){    return i&1?i+1:i-1;    }
int calc(int i,int j,int n){    return (i-1)*n+j;    }

struct Edge{
    int from,next,to,val,flow,dis;
}edge[maxm];
int head[maxn],num;
inline void addedge(int from,int to,int val,int dis){
    edge[++num]=(Edge){from,head[from],to,val,0,dis};
    head[from]=num;
}
inline void add(int from,int to,int val,int dis){
    addedge(from,to,val,dis);
    addedge(to,from,0,-dis);
}

bool vis[maxn];
int dst[maxn];
int flow[maxn];
int pre[maxn];
int Start,End;

int spfa(){
    memset(vis,0,sizeof(vis));
    memset(dst,127/3,sizeof(dst));
    queue<int>q;    q.push(Start);    dst[Start]=0;    flow[Start]=0x7fffffff;
    while(!q.empty()){
        int from=q.front();    q.pop();    vis[from]=0;
        for(int i=head[from];i;i=edge[i].next){
            int to=edge[i].to;
            if(dst[to]<=dst[from]+edge[i].dis||edge[i].val<=edge[i].flow)    continue;
            pre[to]=i;
            dst[to]=dst[from]+edge[i].dis;
            //printf("%d %d %d\n",from,to,dst[to]);
            flow[to]=min(flow[from],edge[i].val-edge[i].flow);
            if(vis[to])    continue;
            vis[to]=1;    q.push(to);
        }
    }
    if(dst[End]==dst[End+1])    return 0;
    int now=End;
    while(now!=Start){
        int ret=pre[now];
        edge[ret].flow+=flow[End];    edge[count(ret)].flow-=flow[End];
        now=edge[ret].from;
    }
    return dst[End];
}

int q[maxn][maxn];

int main(){
    int n=read(),k=read();    End=maxn-10;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j){
            q[i][j]=read();
            add(calc(i,j,n),calc(i,j,n)+n*n,1,-q[i][j]);
            add(calc(i,j,n),calc(i,j,n)+n*n,0x7fffffff,0);
        }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j){
            if(i^n)    add(calc(i,j,n)+n*n,calc(i+1,j,n),0x7fffffff,0);
            if(j^n)    add(calc(i,j,n)+n*n,calc(i,j+1,n),0x7fffffff,0);
        }
    add(Start,calc(1,1,n),0x7fffffff,0);
    add(calc(n,n,n)+n*n,End,0x7fffffff,0);
    int ans=0;
    for(int i=1;i<=k;++i)    ans+=-spfa();
    printf("%d",ans);
}

原文地址:https://www.cnblogs.com/cellular-automaton/p/8317922.html

时间: 2024-08-01 06:19:31

【Luogu】P2045方格取数加强版(最小费用最大流)的相关文章

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

Luogu1006 传纸条 与 Luogu P2045方格取数加强版 其实就是这几道题 在一个有m*n 个方格的棋盘中 每个方格中有一个正整数 现要从在方格中从左上角到右下角取数,只能向右或向下走 每走到一个格子就可以把这个位置上的数取走(下次经过就没有了) 1.让你走1次,求取出的数的总和最大是多少 2.让你走2次,求取出的数的总和最大是多少 3.让你走k次,求取出的数的总和最大是多少 对于第一问,十分显然. 设\(f[i][j]\)表示\(i\)行\(j\)列的最大价值,转移即可. 第二问,

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

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

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

[CodeVs1227]方格取数2(最大费用最大流)

网络流24题的坑还没填完就来搞其他题,你真的要TJ? 写这题给自己的费用流攒个模板. 题目大意:一个n*n的矩阵,每格有点权,从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大. 啊简单的费用流.每个点i拆成i和i',连一条容量为1的边价值为点权,再连一条容量inf的边价值为0来让这个点能被经过,然后S连(1,1)容量k价值0,i'和右.下的点连容量inf价值0的边,(n,n)'连T容

HDU 3376 &amp;&amp; 2686 方格取数 最大和 费用流裸题

题意: 1.一个人从[1,1] ->[n,n] ->[1,1] 2.只能走最短路 3.走过的点不能再走 问最大和. 对每个点拆点限流为1即可满足3. 费用流流量为2满足1 最大费用流,先给图取负,结果再取负,满足2 #include <stdio.h> #include <string.h> #include <iostream> #include <math.h> #include <queue> #include <set&

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和一个价

【Codevs1227】方格取数2(费用流)

题意:给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000) 现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0, 这样一共走K次,现在要求K次所达到的方格的数的和最大. n<=50,k<=10 思路:费用流 将每个点裂成一个出点和一个入点(i,j,1..2),这个思路与最大流类似 (i,j,1)->(i,j,2) 连两条边: 容量为1,费用为a[i,j] 容量为K,费用为0 (i,j,2)->

[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