There are N person and N job to do. The i-th person do j-th job will cost different certain money. Choose a scheme that every person only do one different job, and total cost is minimum.
Input
Input consists of several test cases. The first line of each case is a positive interger N(2<=N<14), it is the number of persons (and jobs). The next N line include N integers, the cost of i-th person doing j-th job is the j-th element in line i.Output
For each input N, you should the minimum cost in one line.Sample Input
3 10 93 73 12 69 40 88 62 76
Sample Output
112
状态压缩DP。因为N比较小,所以容易想到状态压缩DP。
用2进制来表示工作的分配状态,以N=5为例
比如01011,表示1-5份工作,未分配,分配,未分配,分配,分配
如果在01011的状态基础上给第4个人分配工作,那么新的状态可以有11011、01111,要更新这几个状态的解。
反过来想,如何求状态01111的最优解,那么只需从子问题中的最优解中求即可,不需要关心子问题的最优解是如何达到的。
如01111状态相关的状态(子问题)有:
01110(在2,3,4个工作已分配的情况下,第4个人选第5份工作,即可达到状态01111)
01101(在2,3,5个工作已分配的情况下,第4个人选第4份工作,即可达到状态01111)
01011(在2,4,5个工作已分配的情况下,第4个人选第3份工作,即可达到状态01111)
00111(在3,4,5个工作已分配的情况下,第4个人选第2份工作,即可达到状态01111)
#include<stdio.h> #include<memory.h> #include<limits.h> int m[14][1<<14];//m[i][j]表示在前i个人已分配工作,工作分配状态是j的二进制表示的最优解 bool flag[14][1<<14];//flag[i][j]标记状态是否可达或合法 int a[14][14];//存储输入 int main() { int n, i, j, k; while(scanf("%d",&n) != EOF) { memset(flag, false, sizeof(flag)); int max = 1<<n;//最大的状态是max-1,即n位全为1,用max表示状态上限 for(i = 1; i <= n; i++) for(j = 1;j <= n; j++) scanf("%d", &a[i][j]); for(i = 1; i <= n; i++) for(j = 0; j< max; j++) m[i][j] = INT_MAX; m[0][0] = 0; flag[0][0] = true; for(i = 1; i <= n; i++) for(j = 0;j < max; j++)//对前i-1个人已分配工作的所有可达状态处理 { if(!flag[i-1][j]) continue;//如果(i-1,j)不是有效状态跳过 for(k = 1;k <= n; k++)//对第i个人分配可以分配的工作 { int mask = 1<<(k-1); if(j&mask) continue;//第k个工作已分配,跳过 if(m[i-1][j]+a[i][k] < m[i][j|mask])//将第k个工作分配给第i个人,同时更新所达到状态的解 { m[i][j|mask] = m[i-1][j]+a[i][k]; flag[i][j|mask] = true; } } } printf("%d\n", m[n][(1<<n)-1]); } return 0; }