状压dp:金陵十三钗

题目描述
在电影《金陵十三钗》中有十二个秦淮河的女人要自我牺牲代替十二个女学生去赴日本人的死亡宴会。为了不让日本人发现,自然需要一番乔装打扮。但由于天生材质的原因,每个人和每个人之间的相似度是不同的。由于我们这是编程题,因此情况就变成了金陵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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值