工作分配问题 SDUT OJ 搜索算法 回溯

工作分配问题

Description

设有n件工作分配给n个人。将工作i分配给第j个人所需的费用为 cij。试设计一个算法,为每一个人都分配1 件不同的工作,并使总费用达到最小。
设计一个算法,对于给定的工作费用,计算最佳工作分配方案,使总费用达到最小。

Input

输入数据的第一行有1 个正整数n (1≤n≤20)。接下来的n行,每行n个数,表示工作费用。

Output

将计算出的最小总费用输出。

Sample

Input 

3
10 2 3
2 3 4
3 4 5

Output 

9

 

解题思路:

本题有n件工作分配给n个人,第一个人有n个选择,第二个人就只有n-1个选择了,第三个人就只有n-2个选择了,所以总共会n的阶乘次判断(n!)

以每个人为一层节点建立一个多叉树,深度搜索dfs该多叉树 就是 对于每个人分配一份工作,

然后用sum记录该遍历过程(该分配工作的过程)产生的费用,

设置一个min_sum记录所有分配过程产生的 最小分配费用,

每次dfs函数遍历到n,即分配到最后一个人的工作时,表示分配完成, 判断此时sum 和 min_sum 的大小,若sum小,则更新min_sum。

遍历过程中,只有当 未分配到第n个人(最后一个人) 并且 sum还小于min_sum 时,才会向深层遍历(给下一个人分配工作),

因为如果 未分配到最后一个人时就已经费用sum大于min_sum了,再往下遍历也没意义,  因为我们要找最小费用,此时已经大于我们之前找到的最小费用了。 

这个条件相当于“剪枝”。

 

当dfs遍历到n(最后一个人)时,我们需要返回上层,给上层的人分配其他工作,尝试搜索更小的费用

因此,我们需要将刚才分配方式的工作收回,用来尝试别的分配方式,

所以设置一个vis[]数组,vis[j]记录第j个工作是否被分配,被分配则置1,

在dfs遍历到n后,返回上层时,将vis[j]置0,并且将刚才分配方式的sum 减去 arr[pos][j](arr[pos][j]表示第pos个人做第j件工作的费用)

 

参考:
https://www.cnblogs.com/chenhanwu/p/10148476.html

#include <iostream>
#include <cstdio>/// scanf()  printf()
using namespace std;
/***

参考:
https://www.cnblogs.com/chenhanwu/p/10148476.html
***/

int arr[25][25];///记录将工作i分配给第j个人所需的费用为 cij
int vis[25];///标记第i个工作是否被选取,是则置1
int n;///设有n件工作分配给n个人
int sum;///记录当前分配方式的总费用
int min_sum;///记录保存下来的最小费用
void dfs(int pos);///搜索函数,参数为第pos个人
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
    {///i 和 j 都从 1开始记录,到n ,方便理解
        for(int j = 1; j <= n; j++)
        {///本题中的i和j的for循环都是从1到n,千万别忘了 <= n,否则遍历不到n
            scanf("%d", &arr[i][j]);
        }
    }
    sum = 0;///初始sum置0
    min_sum = 0x3f3f3f3f;///初始min_sum置为一个很大的值
    dfs(1);///从第一个人开始搜索
    cout << min_sum << endl;
    return 0;
}
void dfs(int pos)
{
    if(pos > n)
    {///若所有人的工作都已分配完成
        if(sum < min_sum)
        {///并且该组分配方式的费用小于之前记录的最小费用
            min_sum = sum;///则更新最小费用min_sum
        }
    }
    if(sum > min_sum)
    {///如果该组分配方式产生的费用大于之前记录的最小费用,
        return;///则不向深层遍历,相当于剪枝
    }
    for(int j = 1; j <= n; j++)///j表示第j件工作
    {///这里j从1到n,千万别忘记 j <= n,否则遍历到最后的j = n时会直接跳出来
        if(vis[j] == 0)///如果第j件工作未被选取
        {
            vis[j] = 1;///标记为第j件工作被选取
            ///更新当前分配方式的总费用
            sum += arr[pos][j];///arr[pos][j]表示第pos个人做第j件工作的费用
            dfs(pos + 1);///继续向深层遍历
            ///当前分配方式遍历结束后,向上回溯,搜索其他分配结果
            sum -= arr[pos][j];
            vis[j] = 0;
        }
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值