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

原文地址:https://www.cnblogs.com/Forever-666/p/11182637.html

时间: 2024-10-15 12:16:43

XJOI夏令营501训练1——分配工作的相关文章

分配工作时需要考虑的问题

文章试图总结作为一个技术管理者给下属进行工作分配时,需要从哪些方面考虑,以及需要注意的问题.实际上,也可以作为一个下属如何完成上级分配工作的一个指引.就像文章里说到的,我们在分配给别人任务的时候,别人也在分配任务给我们.我们在别人身上寻找某种特质的时候,别人也在我们身上寻找类似的东西. 一般情况下,管理者都会将工作安排给自己信任的人,因为对他的工作能力和工作态度都比较熟悉,能预估到他的工作情况,不会出现太大的意外,有更大的掌控,所以才偏向于自己熟悉的人. 那面对还没有建立起信任关系的人,应该如何

员工分配工作(深度优先)

题意:公司分配N项工作给N个员工,每个员工只能被分给1项工作,每个人处理工作的时间不同.求完成所有工作所需的最少时间. 输入:第一行输入整数N,代表N个员工,员工编号从1到N(1<=N<=10)   接下来输入一个N*N的二维矩阵task[N][N],其中task[i][j](0<=task[i][j]<=1000)代表第i项工作由j号员工完成所需的时间. 输出:输出一个整数,代表工作完成所有工作所需的最少时间. 样例: 输入: 610 11 12 11 9 1111 9 10 1

【YZOJ 1060】分配工作

Description Zxc接到一个任务--拯救世界.好在他有N个粉丝,Zxc把世界分成N个部分,每个粉丝处理一个部分,最后他来收尾.已知每个粉丝处理每个部分所需的时间,求拯救世界的最短时间是多少.每个人同时开始,但是不能互相帮忙.也就是说每个粉丝只能处理一个部分,并且一定要处理一个. Input Format 第一行一个整数N,表示Zxc粉丝的个数.接下来N行,每行N个整数.第i+1行,第j个数表示第i个粉丝第j个部分所需要的时间. Output Format 一个整数,表示最少需要的时间.

Ka的回溯编程练习 Part4|分配工作

设有A,B,C,D,E五人从事J1,J2,J3,J4,J5五项工作,每人只能从事一项,他们的效益如下. 每人选择五项工作中的一项,在各种选择的组合中,找到效益最高的的一种组合输出. 这个主要是细节了,没什么难度 1 #include<stdio.h> 2 int MansToJobs[6][6]={{0,0,0,0,0,0},{0,13,11,10,4,7},{0,13,10,10,8,5},{0,5,9,7,7,4},{0,15,12,10,11,5},{0,10,11,8,8,4}}; 3

xjoi 省选训练23_B

这场我tm 爆零了!!!!! 发现这题是一个基环树森林 拓扑排序去掉 不在环上的节点以后 只剩下一个环 那么这题就成了 环上的覆盖问题 算法1: 倍增 嗯...我好想不会写 算法2: 选取k个点 每一次 走k个点 那么每一次 就只会走 [n/k]步 可以先预处理出每一步 走了以后 你能走到哪里 那么对于每一个环 我们只需要 进行O(n) 就可以做到 这个环的最小覆盖 可惜我比较NAIVE 没有写这个东西 算法3: 真不好意思 这个是最烙算法 我还坑害了我边上的z1j1n1大爷 捂脸熊 发现以前做

XJOI网上同步训练DAY2 T2

[问题描述] 火车司机出秦川跳蚤国王下江南共价大爷游长沙.每个周末勤劳的共价大爷都会开车游历长沙市. 长沙市的交通线路可以抽象成为一个 个点 条边的无向图点编号为 到 任意两点间均存在恰好一条路径显然两个点之间最多也只会有一条边相连.有一个包含一些点对 的可重集合 共价大爷的旅行路线是这样确定的每次他会选择 中的某一对点 并从 出发沿着唯一路径到达 . 小L是共价大爷的脑残粉为了见到共价大爷的尊容小L决定守在这张图的某条边上等待共价大爷的到来.为了保证一定能见到他显然小L必须选择共价大爷一定会经

XJOI网上同步训练DAY1 T3

思路:一开始看到这题的时候想DP,可是发现貌似不行..因为有前缀也有后缀,而且有的后缀会覆盖到现在的前缀,这就不满足无后效性了啊! 但是有个很巧妙的思路:如果我们知道a[i]的最大值,那么p的数量和q的数量也确定了.所以序列长度也确定了,设m为序列长度. 而且对于每个a[i]都代表了一个固定数量的p和q和长度. 因此,长度大于m/2的前缀,我们可以用总的p和总的q减去它,转换成小于等于m/2长度的前缀后缀. 这样我们可以设计DP为f[i][j][k],代表从左往右i个中有j个p,从右往左i个有k

XJOI网上同步训练DAY2 T1

[问题描述] 为了迎接校庆月亮中学操场开始施工.不久后操场下发现了很多古墓这些古墓中有很多宝藏.然而学生们逐渐发现自从操场施工之后学校的运气就开始变得特别不好.后来经过调查发现古墓下有一个太守坟由于操场施工惊动了太守所以学校的运气才会特别不好. 你——月亮中学的学生之一为了拯救学校在梦中和太守进行了沟通.太守说“只要你能解决这个问题我就保佑你们从此事事顺心.你看操场下的古墓中有 个宝藏编号为 到 .现在你必须选择宝藏的一个集合可以不选或者全选.我有两种条件第一种条件有 个每一种条件形如‘如果你选

XJOI网上同步训练DAY6 T2

思路:记得FJ省队集训好像有过这题,可是我太弱了,根本不懂T_T 1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 int n,m; 7 double f[1005][1005]; 8 int pd[1005][1005]; 9 int read(){ 10 int t=0,f=1;char c