joj 2324 Go Dutch

WalkingInTheWind發表於2013-04-11
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;
}