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

任务分配问题

设有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].

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章