Luogu P4014 分配问题 题解

Posted theshadow

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P4014 分配问题 题解相关的知识,希望对你有一定的参考价值。

闲扯

蒟蒻的第一道自己想出怎么建图的题!!虽然是一个没什么技术含量的图 想了想,还是写篇题解纪念一下。

题面

题面

Solution

要求最小费用和最大费用,同时限制了流量,考虑费用流。

虚拟一个超级源点,从这个点分别向 \(N\) 个任务连一条流量为 \(1\) ,费用为 \(0\) 的边。

虚拟一个超级汇点,才从 \(N\) 个物品分别向该点连一条流量为 \(1\) ,费用为 \(0\) 的边。

因为每个人只能做一件,且每个工作只能做一次,所以连的边流量都为一。而第 \(i\) 个人做第 \(j\) 个任务获得的贡献为 \(val_i,j\) ,所以从第 \(j\) 个物品向第 \(i\) 个人连一条费用为 \(val_i,j\) 的边。

如果是求最小费用最大流,那么直接跑模板。

如果是求最大费用最大流,只需要连边时将费用换为负数,求一个最小费用最大流,然后答案再取一个相反数即可。(这个处理好秒啊,自己没想出来,还是看了题解)

Code

#include<bits/stdc++.h>
#define del(a,i) memset(a,i,sizeof(a))
#define ll long long
#define inl inline
#define il inl void
#define it inl int
#define ill inl ll
#define re register
#define ri re int
#define rl re ll
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
using namespace std;
template<class T>il read(T &x)
    int f=1;char k=getchar();x=0;
    for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
    for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
    x*=f;

template<class T>il print(T x)
    if(x/10) print(x/10);
    putchar(x%10+'0');

ll mul(ll a,ll b,ll mod)long double c=1.;return (a*b-(ll)(c*a*b/mod)*mod)%mod;
it qpow(int x,int m,int mod)
    int res=1,bas=x%mod;
    while(m)
        if(m&1) res=(res*bas)%mod;
        bas=(bas*bas)%mod,m>>=1;
    
    return res%mod;

int n,s,t,head[205],num_edge=-1,pre[205],last[205],flow[205],dis[205],mn_cost,val[105][105];
struct Edge
    int next,to,w,c;
    Edge()
    Edge(int next,int to,int w,int c):next(next),to(to),w(w),c(c)
edge[30000];
il add_edge(int u,int v,int w,int c)
    edge[++num_edge]=Edge(head[u],v,w,c),head[u]=num_edge;
    edge[++num_edge]=Edge(head[v],u,0,-c),head[v]=num_edge;

bool tr[205];
inl bool SPFA(int s,int t)
    queue<int> q;q.push(s);
    del(dis,0x3f),del(flow,0x3f);
    dis[s]=0,pre[t]=-1,tr[s]=1;
    while(!q.empty())
        ri pos=q.front();q.pop(),tr[pos]=0;
        for(ri i=head[pos];i!=-1;i=edge[i].next)
            if(dis[edge[i].to]>dis[pos]+edge[i].c&&edge[i].w>0)
                dis[edge[i].to]=dis[pos]+edge[i].c;
                pre[edge[i].to]=pos,last[edge[i].to]=i;
                flow[edge[i].to]=min(flow[pos],edge[i].w);
                if(!tr[edge[i].to]) q.push(edge[i].to),tr[edge[i].to]=1;
            
    
    return pre[t]!=-1;

il MCMF(int s,int t)
    while(SPFA(s,t))
        mn_cost+=dis[t]*flow[t];
        for(ri u=t;u^s;u=pre[u]) edge[last[u]].w-=flow[t],edge[last[u]^1].w+=flow[t];
    

int main()

//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    read(n),del(head,-1),t=2*n+1;
    for(ri i=1;i<=n;++i)
        for(ri j=1;j<=n;++j)
            read(val[i][j]);
            add_edge(j,i+n,1,val[i][j]);
        
    for(ri i=1;i<=n;++i) add_edge(s,i,1,0);
    for(ri i=1;i<=n;++i) add_edge(i+n,t,1,0);
    MCMF(s,t);
    printf("%d\n",mn_cost);
    del(head,-1),num_edge=-1,mn_cost=0;
    for(ri i=1;i<=n;++i)
        for(ri j=1;j<=n;++j)
            add_edge(j,i+n,1,-val[i][j]);
    for(ri i=1;i<=n;++i) add_edge(s,i,1,0);
    for(ri i=1;i<=n;++i) add_edge(i+n,t,1,0);
    MCMF(s,t);
    printf("%d",-mn_cost);
    return 0;

总结

这道题就是一个板子题,用来入门外加熟悉模板的。

网络流的建图方式千千万,真的好神奇的,不要满足于现在的成就,还是要多练题,找到自己做网络流的套路呢~~

以上是关于Luogu P4014 分配问题 题解的主要内容,如果未能解决你的问题,请参考以下文章

P4014 分配问题 网络流

P4014 分配问题

费用流网络流24题P4014 分配问题.md

P4014 分配问题(网络流24题 最大最小费用流)

luogu P1549 棋盘问题 题解

[题解/模板]luogu_P3942_(树上覆盖问题