P2045 方格取数加强版题解

题目链接:P2045 方格取数加强版 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目:

出一个 n*n 的矩阵,每一格有一个非负整数 A{i,j}且A{i,j} <=10^3 现在从 (1,1) 出发,可以往右或者往下走,最后到达 (n,n),每达到一格,把该格子的数取出来,该格子的数就变成 0,这样一共走 K 次,现在要求 K 次所达到的方格的数的和最大。

解析:

一次方格取数原题是一个四位dp,但走k次的限制很难用dp在图上转移。

因为我们看到题目对每条边有限制,考虑用网络流,拆点建图来满足条件。

拆点可以得到两条边,(i,j)的入点向(i,j)的出点连一条费用为-1*(i,j)的值,流量限制为1(乘-1的原因是我们只能算最小费用最大流,这道题最大费用最大流,所以将费用取为负数,就可以做了),以及一条(i,j)的入点向(i,j)的出点连一条费用为0,流量限制为k-1的边。

同时由题意可知,(i,j)的出点向(i,j+1)和(i+1,j)连一条边,边的费用为0,流量限制为k。

再由网络流的性质,建超级源点向起点的入点连边和终点的出点向汇点连边。这些边的费用为0,流量限制为k,仅仅意味联通。

最后跑最小费用最大流即可

#include <bits/stdc++.h>
using namespace std;
const int N=100010;
const int M=300010;
const int INF=2139062143;
int n,k,dis[N],pre[N],vis[N],flow[N],go[M],nxt[M],hd[N],jz[M],fl[M],tot=1;
int id(int x,int y)
{
    return (x-1)*n+y;
}
bool spfa(int s,int t)
{
    memset(dis,0x7f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    queue<int> q;
    q.push(s);
    dis[s]=0;
    vis[s]=1;
    flow[s]=INF;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=hd[u];i;i=nxt[i])
        {
            int v=go[i];
            if(fl[i]==0)continue;
            if(fl[i]>0&&dis[v]>dis[u]+jz[i])
            {
                dis[v]=dis[u]+jz[i];
                flow[v]=min(flow[u],fl[i]);    
                pre[v]=i;
                if(!vis[v])
                {
                    q.push(v);
                    vis[v]=1;                        
                }
            }
        }
    } 
    if(dis[t]==INF)return 0;
    return 1;
}
long long ans=0,maxflow;
void zdfy(int s,int t)
{
    while(spfa(s,t))//图s,t是否联通 
    {
        maxflow+=flow[t]; 
        ans+=dis[t]*flow[t];//一趟的价值为流量乘从s开始走的边的价值 
        int x=t;
        while(x!=s)
        {
            int las=pre[x];
            fl[las]-=flow[t];//注意流量只消耗了flow[t] 
            fl[las^1]+=flow[t];
            x=go[las^1]; 
        }    
    }
} 
void add(int x,int y,int j,int f)
{
    nxt[++tot]=hd[x];go[tot]=y,jz[tot]=j,fl[tot]=f,hd[x]=tot;
    nxt[++tot]=hd[y];go[tot]=x,jz[tot]=-j,fl[tot]=0,hd[y]=tot;
    return ;
}//dinic算法要反向边 
int main()
{
    scanf("%d%d",&n,&k);
    int s=0,t=2*n*n+1;
    add(s,id(1,1),0,k);
    add(id(n,n)+n*n,t,0,k);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            int x;scanf("%d",&x);
            add(id(i,j),id(i,j)+n*n,-x,1);
            add(id(i,j),id(i,j)+n*n,0,INF);
            if(i<n)add(id(i,j)+n*n,id(i+1,j),0,INF);
            if(j<n)add(id(i,j)+n*n,id(i,j+1),0,INF);
        }
    //建图 
    zdfy(s,t);
    printf("%lld\n",-1*ans);//答案取反 
    return 0;
 } 

 

posted @ 2024-01-25 09:07  storms11  阅读(9)  评论(0编辑  收藏  举报