最小费用最大流

题目描述
同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小。

说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。

输入格式
第一行有两个数M,N,表示技术人员数与顾客数。

接下来n行,每行m个整数。第i+1行第j个数表示第j位技术人员维修第i辆车需要用的时间T。

输出格式
最小平均等待时间,答案精确到小数点后2位。

输入输出样例
输入 #1
2 2
3 2
1 4
输出 #1
1.50
说明/提示
(2<=M<=9,1<=N<=60), (1<=T<=1000)

把m个师傅拆成mn个点,第(i,j)个点表示第i个师傅倒数第j个修车,再建n个点表示n辆车,mn个点都与第k辆车相连,花费是jc[k][i],流量为1。然后mn个点与源点相连,流量为1,花费为0,n辆车与汇点相连,流量为1,花费为0。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn = 200005;
const int INF = 2e9+1;
const int inf = 0x7f7f7f7f;
int n,m,cnt = 1;
int head[maxn];
int pre[maxn],cost[maxn],flow[maxn];
int c[105][105];
int maxflow,mincost;
bool vis[maxn];

struct node
{
    int to,next,flow,w;
}l[maxn];
queue<int>q;

void add(int x,int y,int z,int w)
{
    cnt++;
    l[cnt].to = y;
    l[cnt].flow = z;
    l[cnt].w = w;
    l[cnt].next = head[x];
    head[x] = cnt;
    cnt++;
    l[cnt].to = x;
    l[cnt].flow = 0;
    l[cnt].w = -w;
    l[cnt].next = head[y];
    head[y] = cnt;
}

bool spfa(int s,int t)
{
    memset(cost,0x7f,sizeof(cost));
    memset(flow,0x7f,sizeof(flow));
    memset(vis,false,sizeof(vis));
    q.push(s);
    vis[s] = 1,cost[s] = 0,pre[t] = -1;
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int i=head[u]; i; i=l[i].next)
        {
            int v = l[i].to;
            if(l[i].flow && cost[v] > cost[u] + l[i].w)
            {
                cost[v] = cost[u] + l[i].w;
                pre[v] = i;
                flow[v] = min(flow[u],l[i].flow);
                if(!vis[v])
                {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    return cost[t] != inf;
}

void MCMF(int s,int t)
{
    while(spfa(s,t))
    {
        int u = t;
        maxflow += flow[t];
        mincost += flow[t]*cost[t];
        while(u != s)
        {
            l[pre[u]].flow -= flow[t];
            l[pre[u]^1].flow += flow[t];
            u = l[pre[u]^1].to;
        }
    }
}

int main()
{
    int s,t;
    scanf("%d%d",&m,&n);
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            scanf("%d",&c[i][j]);//第i辆车由第j个人修的花费
        }
    }
    s = 1,t = 2;
    for(int i=1; i<=n*m; i++)
    {
        add(s,i+2,1,0);
    }
    for(int i=1; i<=m; i++)//第i个人第j个修车,修第k辆车
    {
        for(int j=1; j<=n; j++)
        {
            for(int k=1; k<=n; k++)
            {
                int now = (i-1)*n+j+2;
                add(now,k+n*m+2,1,c[k][i]*j);
            }
        }
    }
    for(int i=1; i<=n; i++)
    {
        add(n*m+i+2,t,1,0);
    }
    MCMF(s,t);
    printf("%.2f",(double)mincost/(double)n);
}


平面上有 N 个点,每个点可以向比它纵坐标小的点连边,要求连成一颗二叉树,使得边长总和最小。

建图:拆点,源点向每个点的入点连容量为2,费用为0的边,每个点的出点连容量为1,费用为0的边。对于边(u,v),u的入点连向v的出点,费用为距离,容量为1。跑MCMF,若最大流不等于n-1,则无法连成二叉树,否则最小费用即为答案。

有些题目中有某个节点必须经过几次,或是某个东西必须要选的限制。通常通过满流或是带下界的网络流来控制下界,拆点限流来控制上界。
当某个点最多只能和k个点相连时,拆点时令源点向该点连一条容量为k的边,当某个点最多只能被访问k次时,令出点连一条容量为k的点到汇点。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值