XJOI夏令营501训练1——分配工作

传送门:QAQQAQ

 

题意:某公司有工作人员x1,x2,…,xn ,他们去做工作y1,y2,…,ym(n<=m) ,每个人都能做其中的几项工作,并且对每一项工作都有一个固定的效率。问能否找到一种合适的工作分配方案,使得总的效率最高。要求一个人只能参与一项工作,同时一项工作也必须由一个人独立完成。不要求所有的人都有工作。

 

思路:首先让我们明确二分图匹配算法和网络流的区别:二分图KM算法是网络流最小费用最大流中的特例,即KM算法必须满足每一个x点都被匹配才能运行,但两个算法的前提都是在最大匹配的情况下最优,而这道题显然没有要求要最大匹配,所以这两种算法均被否定……(本来想过用最大流做,但发现最大流无法限制x节点只能连一条边)

所以我们要改进KM算法,我们现在要做的是令所有连边都满足最大匹配的前提,这样效率高低就会成为判断方案优劣的唯一因素。(这种控制无关因素的思路值得借鉴)

 

我们现在把每个人干不了的工作都连上0的边,表示这个人干他干他干不了的工作效率为0,这样每个人和每个工作就都有了边,因为m>=n,所以每个人都可以搜到,再跑一遍KM求答案即可。

(其实这种思路来改进最小费用最大流也可以,但很麻烦,因为网络流中残量网络为0时跑不了)

 

上代码:

#include<bits/stdc++.h>
using namespace std;
const int N=505;
const int M=1000001;
const int inf=(int)2e9;
 
int E[505][505];
int match[505],lx[505],ly[505],visx[505],visy[505],slack[505],n,m;
 
bool dfs(int u)
{
    visx[u]=1;
    for(int v=1;v<=m;v++)
    {
        if(visy[v]) continue;
        if(lx[u]+ly[v]==E[u][v])
        {
            visy[v]=1;
            if(match[v]==-1||dfs(match[v]))
            {
                match[v]=u;
                return 1;
            }
        }
        else
        {
            slack[v]=min(slack[v],lx[u]+ly[v]-E[u][v]);
        }
    }
    return 0;
}
 
void EK()
{
    memset(visx,0,sizeof(visx));
    memset(visy,0,sizeof(visy));
    for(int x=1;x<=n;x++)
    {
        for(int i=1;i<=m;i++) slack[i]=inf;
        while(true)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(dfs(x)) break;
            int Delta=inf;
            for(int i=1;i<=m;i++) if(!visy[i]) Delta=min(Delta,slack[i]);
            for(int i=1;i<=n;i++) if(visx[i]) lx[i]-=Delta;
            for(int i=1;i<=m;i++) if(visy[i]) ly[i]+=Delta;
                        else slack[i]-=Delta;
        }
    }
}
 
int main()
{
    memset(E,0,sizeof(E));
    memset(ly,0,sizeof(ly));
    memset(match,-1,sizeof(match)); int k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=k;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        E[x][y]=max(E[x][y],z);
    }
    for(int i=1;i<=n;i++)
    {
        lx[i]=0;
        for(int j=1;j<=m;j++) lx[i]=max(lx[i],E[i][j]);
    }
    EK();
    int ans=0;
    for(int i=1;i<=m;i++)
    {
        if(match[i]!=-1) ans+=E[match[i]][i];
    }
    cout<<ans<<endl;
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/Forever-666/p/11182637.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值