[日常摸鱼]pojKaka's Matrix Travels-拆点+最大费最大流

方格取数的升级版,每个格子最多取一次。

$k=1$的话就是个普及组的dp题,$k=2$就是在之前的基础上多加两维。

然而现在$k$太大了当然就不dp啦

对于$k=1$的情况我们还可以把$(i,j)$向$(i+1,j),(i,j+1)$连边然后答案就是跑最长路,而对于更大的情况我们的瓶颈在于直接跑最长路不能限制每个点只取一次。

对于点来说没有什么好的方法我们就把问题转移到边上:把每个点拆成一条边。具体的说就是把一个点拆成两个点,把点权变成边权,而我们又要限制这样子的每条边最多走一次,这里就有点费用流的感觉了(雾)。

所以我们可以这样建图:把点$i$拆成$i$和$i'$,不妨令$i$为入点$i'$为出点,从入点到出点连两条边:一条容量为1,费用为点权$c$,另一条容量为$k-1$,费用为0。

原图里的点的出点再向能够直接到达的点的入点连一条容量为$k$,费用为0的边。

这样每次bfs的时候只要成功都会走出一条完整的路径,路径上的费用被算进去之后这条路也就不会再走了(因为有费用的只有容量为1的嘛),于是也就保证了只会取一次并且一定是第一次经过的时候被取走。

以起点的入点为源点,以终点的出点为汇点跑最大费用最大流得到的就是答案啦

#include<cstdio>
#include<queue>
#include<cstring>
#define rep(i,n) for(register int i=1;i<=n;i++)
#define REP(i,a,b) for(reigster int i=a;i<=b;i++)
using namespace std;
const int N=5005;
const int M=20005;
const int INF=(~0u>>1);
struct edge
{
    int to,nxt,w,c;
    edge(int to=0,int nxt=0,int w=0,int c=0):to(to),nxt(nxt),w(w),c(c){}
}edges[M<<1];
int n,k,s,t,ans,cnt;
int head[M<<1],infc[N],vis[N],d[N],pre[N];
queue<int>q;

inline void addEdge(int u,int v,int w,int c)
{
    edges[++cnt]=edge(v,head[u],w,c);head[u]=cnt;
    edges[++cnt]=edge(u,head[v],0,-c);head[v]=cnt;
}
inline int get_num(int i,int j,int p)
{
    return (i-1)*n+j+n*n*p;
}
#define cur edges[i].to
inline bool spfa()
{
    memset(vis,0,sizeof vis);
    rep(i,t)d[i]=-INF;q.push(s);
    d[s]=0;infc[s]=INF;vis[s]=1;
    while(!q.empty())
    {
        int x=q.front();q.pop();vis[x]=0;
        for(register int i=head[x];i;i=edges[i].nxt)if(edges[i].w&&d[cur]<d[x]+edges[i].c)
        {
            d[cur]=d[x]+edges[i].c;
            pre[cur]=i;infc[cur]=min(infc[x],edges[i].w);
            if(!vis[cur])vis[cur]=1,q.push(cur);
        }
    }
    if(d[t]==-INF)return 0;
    return 1;
}
#undef cur
inline void updata()
{
    
    int tmp=t;
    while(tmp!=s)
    {
        int i=pre[tmp];
        edges[i].w-=infc[t];
        edges[i^1].w+=infc[t];
        tmp=edges[i^1].to;
    }
    ans+=d[t];
}
int main()
{
    scanf("%d%d",&n,&k);
    s=1;t=2*n*n;cnt=1;
    rep(i,n)rep(j,n)
    {
        int x;scanf("%d",&x);
        addEdge(get_num(i,j,0),get_num(i,j,1),1,x);
        addEdge(get_num(i,j,0),get_num(i,j,1),k-1,0);
        if(i<n)addEdge(get_num(i,j,1),get_num(i+1,j,0),k,0);
        if(j<n)addEdge(get_num(i,j,1),get_num(i,j+1,0),k,0);
    }
    while(spfa())updata();
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/yoshinow2001/p/8423772.html