任务分配问题
设有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].