洛谷 P2053 [SCOI2007]修车 网络流 最小费用最大流 Dinic+Spfa

题目链接:

https://www.luogu.com.cn/problem/P2053

思路参考博客:

https://www.luogu.com.cn/blog/a23333/solution-p2053

算法:1:最小费用最大流 Dinic+Spfa

图解:

思路:

1:费用流+拆点

2:首先如果一个工人只能维修一辆车,那么就是个裸的二分图带权匹配了。可惜一个工人可以维修多辆车。可以看到 n 比较大( ≤60 ), m比较小( ≤9 )。数据范围提示我们可以把 m个点拆成n个点

3:具体怎么拆:第 x号工人,拆成 x1,x2,x3...xn 共n个工人。然后客户向每个工人连边。如果该工人维修该客户需要 a 元,则向 xi​ 连一条流量为 1,边权为 i∗a 的边

4:解释:如果客户 A最终选择了 x2 这个工人,肯定会有别的客户 B 向 x1​ 匹配。相当于让客户 A 先和 x 工人匹配,再让 B 匹配,所以答案加上了两倍的 x 修 A 的时间:修A,A等了一个修A的时间,因为B也要x这个工人修,因此B在x修A的时候也在等候,等候一个修A的时间

代码:

#include <bits/stdc++.h>

using namespace std;
//const int maxn=1e4+10,maxm=1e5+10;
const int maxn=6e2+2,maxm=6e4+6e3+1;
int m,n,s,t,a,tot=1,head[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn],maxflow,mincost;
bool vis[maxn];
queue<int>q;

struct edge
{
    int to,next,w,dis;
}e[maxm];

void addedge(int x,int y,int w,int dis)
{
    e[++tot].to=y;
    e[tot].w=w;
    e[tot].dis=dis;
    e[tot].next=head[x];
    head[x]=tot;
}

bool spfa(int s,int t)
{
    memset(dis,0x7f,sizeof(dis));
    memset(flow,0x7f,sizeof(flow));
    memset(vis,0,sizeof(vis));
    q.push(s);vis[s]=1;dis[s]=0;pre[t]=-1;
    while(!q.empty())
    {
        int now=q.front();q.pop();
        vis[now]=0;
        for(int i=head[now];i!=-1;i=e[i].next)
        {
            int y=e[i].to;
            if(e[i].w>0&&dis[y]>dis[now]+e[i].dis)
            {
                dis[y]=dis[now]+e[i].dis;
                pre[y]=now;
                last[y]=i;
                flow[y]=min(flow[now],e[i].w);
                if(!vis[y])
                {
                    q.push(y);
                    vis[y]=1;
                }
            }
        }
    }
    return pre[t]!=-1;
}

void dfs()
{
    int now=t;
    maxflow+=flow[t];
    mincost+=flow[t]*dis[t];
    while(now!=s)
    {
        e[last[now]].w-=flow[t];
        e[last[now]^1].w+=flow[t];
        now=pre[now];
    }
}

int main()
{
    ios::sync_with_stdio(0);
    scanf("%d %d",&m,&n);
    s=0,t=m*n+n+1;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=m*n;i++)addedge(i+n,t,1,0),addedge(t,i+n,0,0);
    for(int i=1;i<=n;i++)addedge(s,i,1,0),addedge(i,s,0,0);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
           scanf("%d",&a);
           for(int k=1;k<=n;k++)
           {
             addedge(i,n*j+k,1,k*a),addedge(n*j+k,i,0,-k*a);
           }
        }
    while(spfa(s,t))dfs();
    printf("%.2lf\n",1.0*mincost/n);
    return 0;
}
发布了164 篇原创文章 · 获赞 82 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/aiwo1376301646/article/details/104313451