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

总结

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

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

原文地址:https://www.cnblogs.com/TheShadow/p/11370196.html

时间: 2024-10-20 15:10:42

Luogu P4014 分配问题 题解的相关文章

luogu P4014 分配问题 |费用流

题目描述 有 nnn 件工作要分配给 nnn 个人做.第 iii 个人做第 jjj 件工作产生的效益为 cijc_{ij}cij? .试设计一个将 nnn 件工作分配给 nnn 个人做的分配方案,使产生的总效益最大. 输入格式 文件的第 111 行有 111 个正整数 nnn,表示有 nnn 件工作要分配给 nnn 个人做. 接下来的 nnn 行中,每行有 nnn 个整数 cijc_{ij}cij???,表示第 iii 个人做第 jjj 件工作产生的效益为 cijc_{ij}cij?. 输出格式

P4014 分配问题

\(\color{#0066ff}{题目描述}\) 有 \(n\) 件工作要分配给 \(n\) 个人做.第 \(i\) 个人做第 \(j\) 件工作产生的效益为 \(c_{ij}\) .试设计一个将 \(n\) 件工作分配给 \(n\) 个人做的分配方案,使产生的总效益最大. \(\color{#0066ff}{输入格式}\) 文件的第 \(1\) 行有 \(1\) 个正整数 \(n\),表示有 \(n\) 件工作要分配给 \(n\) 个人做. 接下来的 \(n\) 行中,每行有 \(n\) 个

邮递员送信(luogu 1629)题解

[问题描述] 有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N.由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要一定的时间.这个邮递员每次只能带一样东西.求送完这N-1样东西并且最终回到邮局最少需要多少时间. [样例输入] 5 10    2 3 5    1 5 5    3 5 6    1 2 8    1 3 8    5 3 4    4 1 8    4 5 3    3 5 6    5 4 2 [样例输出] 83

洛谷 P4014 分配问题 【最小费用最大流+最大费用最大流】

其实KM更快--但是这道题不卡,所以用了简单粗暴的费用流,建图非常简单,s向所有人连流量为1费用为0的边来限制流量,所有工作向t连流量为1费用为0的边,然后对应的人和工作连(i,j,1,cij),跑一遍最小费用最大流再跑一遍最大费用最大流即可.方便起见直接重建图了. #include<iostream> #include<cstdio> #include<queue> #include<cstring> using namespace std; const

$Luogu P2029$ 跳舞 题解

一道不是十分水的\(dp\). 首先我们考虑\(dp\)方程的构造.起初我定义的状态是\(dp_{i,j}\)表示前\(i\)个格子,总共跳了\(j\)次的最大得分.但事实上它并不可以转移,因为我们不知道新的一轮操作从之间的哪个格子算起. 那么状态转移方程就出来了,我们把第一维改成本次跳到第\(i\)个格子上,包括本次在内总共跳了\(j\)次的最大得分,那么转移的时候,由于本次一定要跳到\(i\)上(如状态中所定义),所以不用分类讨论.方程就是:\[dp_{i,j}=\max\{dp_{k,j-

Luogu P1342 请柬 题解

差不多是Dijkstra的裸题吧... 这道题可以分为来回两个阶段. 去的时候很简单,直接用一次Dijkstra,然后统计答案. 回来的时候就有些巧妙了,虽然表面上是每个点回到起点,但是何尝不可将其看成从起点出发,逆着每个点过来的路去找一次每个点?所以只需要存边的时候处理一下,然后直接跑Dijkstra就行了. 附上代码. #include<bits/stdc++.h> #define clean(a,i) memset(a,i,sizeof(a)) #define ll long long

Luogu P2014 选课 题解报告

题目传送门 [题目大意] 有n门选修课,每一门课都有固定的学分$S_i$,每个学生可以选m门课.有些选修课有先修课,每一门课最多只有一门先修课,求能获得的最多学分. [思路解析] 设f[x][t]表示在以x结点为根的子树中选t门课能获得的最大学分,x的子结点集合为son[x],子结点个数为p,且对于x的第i个子结点son[i],以其为根结点的子树中选课数量为$C_i$,则转移方程为:$$f[x][t]=max(\sum_{i=1}^{p}f[son[i]][c[i]])+s[i](满足\sum_

luogu P3952 时间复杂度题解

显然这是一道大模拟 我们要做的就是读入一堆字符串,然后模拟这个循环. 定义某一层的复杂度为执行完这一层循环之后,消耗的复杂度. 某层循环的复杂度=\(max \{\)所有并列的下一层循环的复杂度\(\}\).通俗点说,就是在某层循环中有分支的时候,这一层的复杂度=\(max \{\)所有分支的复杂度\(\}+\)本层复杂度. 最后复杂度=\(max \{\)所有的第一层循环复杂度\(\}\) 考虑到会有分支,所以我们采用递归来实现(当然其本质是栈,但是我不会写). 由于要使程序不至于\(RE\)

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

题目描述 有 n 件工作要分配给 n 个人做.第 i个人做第 j 件工作产生的效益为cij? .试设计一个将 n件工作分配给 n 个人做的分配方案,使产生的总效益最大. 输入格式 文件的第 1 行有 1 个正整数 n,表示有 n 件工作要分配给 n 个人做. 接下来的 n 行中,每行有 n 个整数 cij???,表示第 i 个人做第 j件工作产生的效益为cij?. 输出格式 两行分别输出最小总效益和最大总效益. 输入输出样例 输入 #1复制 5 2 2 2 1 2 2 3 1 2 4 2 0 1