任務分配問題-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].

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