题目描述
在电影《金陵十三钗》中有十二个秦淮河的女人要自我牺牲代替十二个女学生去赴日本人的死亡宴会。为了不让日本人发现,自然需要一番乔装打扮。但由于天生材质的原因,每个人和每个人之间的相似度是不同的。由于我们这是编程题,因此情况就变成了金陵n钗。给出n个女人和n个学生的相似度矩阵,求她们之间的匹配所能获得的最大相似度。
所谓相似度矩阵是一个n*n的二维数组like[i][j]。其中i,j分别为女人的编号和学生的编号,皆从0到n-1编号。like[i][j]是一个0到100的整数值,表示第i个女人和第j个学生的相似度,值越大相似度越大,比如0表示完全不相似,100表示百分之百一样。每个女人都需要找一个自己代替的女学生。
最终要使两边一一配对,形成一个匹配。请编程找到一种匹配方案,使各对女人和女学生之间的相似度之和最大。
题目链接:金陵十三钗
输入
第一行一个正整数n表示有n个秦淮河女人和n个女学生
接下来n行给出相似度,每行n个0到100的整数,依次对应二维矩阵的n行n列。
输出
仅一行,一个整数,表示可获得的最大相似度
样例输入
4
97 91 68 14
8 33 27 92
36 32 98 53
73 7 17 82
样例输出
354
分析
看到这道题目很容易想到n皇后问题,去尝试每个位置的可能,这时算法复杂度为O(n!),当n>=12时,算法复杂度会超过1e9,显然这是会超时的。这是我们考虑状压dp与深度优先搜索结合。状压dp即状态压缩动态规划,利用计算机二进制的性质,表示某一种状态。如1001表示选取第一个和第四个,此举会简化选取状态的表示,也是对空间的节约。
关键字: 记忆化搜索 动态规划
(代码来源于学长,并非原创,做了简单注释)
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 18;
int likes[maxn][maxn], dp[maxn][100000];
int n, ans;
int dfs(int row, int val, int k){ //row代表行,k代表当前选择了多少列
if (dp[row][val] != -1)//表示之前已经遇到过当前情况,不需要再次执行
return dp[row][val];
int &d = dp[row][val];
if (k == 1) { //边界
for (int i = 0; i < n; i++){
int x = 1 << i;
if (x & val)
return d = likes[row][i];
}
}
for (int i = 0; i < n; i++){
int x = 1 << i;
if (x & val)
d = max(d, likes[row][i] + dfs(row + 1, val - x, k - 1));
}
return d;
}
int main(){
while (scanf("%d", &n) == 1){
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
cin >> likes[i][j];
memset(dp, -1, sizeof(dp));//设置标记,置位-1
int start = (1 << n) - 1;//全1
printf("%d\n", dfs(0, start, n));
}
return 0;
}