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