DP3 状压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
数据规模和约定
  对于70%的数据,n<=10
  对于100%的数据,n<=13
样例说明
  最大相似度为91+92+93+73=354
  
  问题分析:
  问题很简单,就是每行或者每列选一个数,行或列不能相同,这些数加起来值最大;
  直接可以dfs,对于70%的数据没有问题;想过100%数据的话必须得另寻方案;
  
  状态表示:
  有点类似三角形的dp,可是这题不同,三角形是前i行j列的最大值,j的状态转移很容易找,只有j和j-1;
  而此题不同,首先j不能重复,而且可以跳跃,这就限制了j的条件,而且第一行有多种选择,不好进行
  状态转移;换种方式,每个女学生都有选与不选两种方式,如4个学生有2的4次方种,1001表示第一和
  第四位已被选择;涉及到很多位运算,可以看一些位运算这一块,那么可以定义一个dp[i[[j]表示当前进
  行到第i个学生的配对,当前的状态是j,此时的值为dp[i][j];
  
  状态转移:
  dp[i][j]=max(dp[i][j],dp[i-1][t]+like[pos][i])其中t状态是j的前一个状态,pos是当前的选择;
  t状态加上当前的pos选择即可到达j的状态;如111的状态,到达它有3种可能,110 ,101 ,011;
  分别对应(从右到左)选第一个学生,选第二个学生,选第三个学生;
   枚举方案:先枚举i再是状态一直到所有的状态11111……;每种状态又可有多种状态转移而来;
   所以有3个for循环;
  
   代码如下:

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=15;
int like[N][N];int dp[N][1<<N]={0};//最多有2的N次方种状态; 
int count_one(int num)//此函数返回的是int类型的数的二进制有多少个1
{
 int t=0;
 while(num)
 {
  if(num&1)
  t++;
  num>>=1;
 }
 return t;
}
int lastone(int num)//此函数返回的是二进制数最后一位1的位置;
{
    int pos=0;
    while(num){
        pos++;
        if(num&1)
            return pos;
        num>>=1;
    }
    return pos;
}
int main()
{
 int n;
 cin>>n;
 for(int i=1;i<=n;i++)
  for(int j=1;j<=n;j++)
     cin>>like[i][j];
    for(int i=1;i<=n;i++)
    {
     for(int j=0;j<=(1<<n);j++)
     {
      if(count_one(j)==i)//这个判断很重要,只有当前的j状态1的数目和i值相等时才有状态转换;
      { 
         int nowstate=j;//记录当前的状态;
          for(int k=1;k<=i;k++)//枚举哪些可以到j状态,共i个状态
         { 
              int pos=lastone(nowstate);//枚举每个1的位置
              int t=j&(~(1<<(pos-1)));//t是前一个状态;
              int cor=nowstate&(~(1<<(pos-1)));//直到最左边的1被确定
              dp[i][j]=max(dp[i][j],dp[i-1][t]+like[pos][i]);
              nowstate=cor//每次更新nowstate,如1111直至更新为1000到0000
      }
   }
  }
 }
 int f=pow(2,n)-1;//输出全为1的状态的第in个选择的dp[n][f];
 cout<<dp[n][f]<<endl;
 return 0;
} 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值