任务分配问题-DFS\位运算

本文探讨了一个任务分配问题,旨在找到最佳的人任务匹配以最小化完成所有任务的总时间。针对n个人和n种任务的情况,提出了使用深度优先搜索(DFS)和位运算的方法,并给出了相应的代码实现。通过剪枝策略优化了搜索效率,以确保找到最短完成时间。文章附带了一个测试用例及相应输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

任务分配问题

设有n个人,每个人都可以完成n种不同的任务,但所需时间不同。如果只需一人去完成每一项工作,则应如何分配n个人并使完成所有n项工作的总时间为最小.

只有一组测试用例。
输入:第一行是操作员的人数n(4=<n<=13),接下来的n行里每行有n个数,分别表示第i名操作员完成第i项任务的时间。
输出:完成所有任务的最短时间。

用例输入:

4

3   8   4  12

9  12 13  5

8   7   9   3

12 7   6   8

用例输出:

21

源代码一:DFS

#include<stdio.h>
#define INF 999999999

int  N , flag[14] , project[14][14] , min[14] , best=INF;
void dfs( int i , int sum );

int main( )
{
	int  i , j;

	scanf("%d",&N);

	for( i=1 ; i<=N ; i++ )
		for( j=1 , min[i]=INF ; j<=N ; j++ )
			scanf("%d",&project[i][j]);

	for( i=1 ; i<=N ; i++ )
		for( j=1 ; j<=N ; j++ )
			if( project[i][j] < min[j] )
				min[j] = project[i][j];	//找到每一列的最小值
	;
	dfs( 1 , 0 );	//从第一个人开始搜索

	printf("%d\n",best);

	return 0;
}


void dfs( int col , int sum )
{
	int k , j , minSum;

	if( col==N+1 ){	//达到了搜索深度
		if( sum<best )
			best = sum;
		return ;
	}

	for( int j=1 ; j<=N ; j++ ){
		if( project[col][j]+sum<best && !flag[j] ){
			for( minSum=sum+project[col][j] , k=1 ; k<=N ; k++ )	//优化,有点类似分支限界法,若没选中的任务列最小值之和比best大,那就直接剪掉了
			{
				if( !flag[k] && k!=j )
					minSum += min[k];
			}
			if( minSum<best )
			{
				flag[j] = 1;	//标记
				dfs( col+1 , sum+project[col][j] );	//继续搜索
				flag[j] = 0;	//回溯
			}
		}
	}
}

代码分析:代码采用了DFS的搜索算法,程序很短,不过还是需要合适剪枝的,有点类似分支限界法,若没选中的任务列最小值之和比best大,那就直接剪掉了,这样的话能够剪掉好多不必要的时间花销。


源代码二<位运算>:

#include<stdio.h>
#define INF 999999999

int possible( int num , int k , int n );

int main( ){
	int n , i , j , k;
	int dp[1<<14] , map[20][20];

	while( ~scanf("%d",&n) ){
		for( i=1 ; i<=n ; i++ )
			for( j=1 ; j<=n ; j++ )
				scanf("%d",&map[i][j]);
	
		for( i=1 , dp[0]=0 ; i<(1<<n) ; i++ )//dp[0]=0
			dp[i] = INF;	//初始化最大值

		for( i=1 ; i<=n ; i++ ){	//第i个人开始从事活动
			for( j=1 ; j<=(1<<n)-1 ; j++ ){	//对每种可能情况
				if( possible( i , j , n ) ){	//合法配对
					for( k=0 ; k<=n-1 ; k++ ){	//寻找每一个有1的状态
						if( ( (1<<k)&j ) && dp[j-(1<<k)]+map[i][n-k]<dp[j] )
							dp[j] = dp[j-(1<<k)]+map[i][n-k];
					}
				}
			}
		}

		printf("%d\n",dp[(1<<n)-1]);
	}

	return 0;
}


int possible( int num , int k , int n ){	//数k二进制中1的数目是否和num一致
	int i , sum=0;

	for( i=0 ; i<=n-1 ; i++ ){
		if( (1<<i) & k )
			sum++;
	}

	return sum==num ? 1 : 0;
}


PS:计算一个数字对应的二进制中1个个数:

int bitNum( int n ){
	int sum=0;

	while( n ){
		n &= ( n-1 );	//每次都把最后的一位1减去
		sum++;
	}

        return sum;
 }


算法分析:采用了位运算来解决问题,01010表示n为5个人5个活动,前两个人做了2和4活动消耗的时间最小值(即响应位为1表示对应活动已做),则有状态专业方程:dp[j]=dp[j-(1<<k)]+map[i][n-k](n-k是因为k为0时即1往左移一位1,表示第四个人)(1<<k为1时才使用该方程),最后输出dp[11111]即dp[1<<n-1].

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值