题目链接:[点这儿].
题意:
在一个
n X n
的矩阵中,选择n
个数,这n
个数所在的行列各不相同,求出这n
个数的最大和. n<=16 .
解析:
看到这个题第一想法就是用
KM
算法做,能用KM
算法做,那么就一定能用最小费用最大流做,这里不讨论这两种做法,讨论下动态规划的做法.首先,这是个状态压缩动态规划的裸题,这里如果用搜索去做,会超时,而且会爆内存空间,因此,得把状态压缩到一个数中,正好,每一列选或不选对应
0
和1
,故可以把这个状态压缩到16
位的二进制数中;设
dp[i][j]
表示1~i
行状态为j
的最大和,这个时候只有j
中二进制中1的个数等于i
的j
状态是有效的,因为到第i
行,只能选择i
列,于是我们对合法的状态进行枚举,同时也枚举j
状态中那么二进制位为0
的列,那么很明显,状态转移方程为:dpi+1,j|(1<<ibit)=max{dpi,j+arri,n−ibit−1}(j&(1<<ibit)==0).
这种写法是由当前状态推下一种状态,而我们写得比较多的是前一种状态推当前状态.两种都要掌握.
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int T, K = 1;
for (scanf("%d", &T); T--; K++) {
int n, x;
scanf("%d", &n);
vector<vector<int> > arr(n + 1, vector<int>(n)), dp(n + 1, vector<int>(1 << n, 0));
for (int i = 1; i <= n; i++)
for (int j = 0; j < n; j++)
arr[i][j] = (scanf("%d", &x), x);
for (int i = 1; i <= n; i++) {
for (int j = 0; j < (1 << n); j++) {
int cntOfOne = 0;
for (int ibit = 0; ibit < n; cntOfOne += (j & (1 << ibit)) ? 1 : 0, ibit++);
if (cntOfOne != i - 1)
continue;
for (int ibit = 0; ibit < n; ibit++)
if (!(j & (1 << ibit)))
dp[i][j | (1 << ibit)] = max(dp[i][j | (1 << ibit)], dp[i - 1][j] + arr[i][n - ibit - 1]);
}
}
printf("Case %d: %d\n", K, dp[n][(1 << n) - 1]);
}
return 0;
}