思路 :
这道题最基本的做法就是DFS直接暴力破解, 这样的复杂度毫无疑问的O( n! ), 是不能完全AC的. 那么, 看到这道题问的是最优解, 那么想必跟动态规划能扯上关系了, 但是咋一看, 转移方程可不太好写, 一开始的时候我还写了个错的转移式, 妄想能在O(n^2)内求解...*_*...
言归正传, 使用动态规划的话, 要注意的是 : 在为第 i 个妓女匹配时, 需要在前 i-1 个妓女的所有匹配情况中作综合规划, 那么, 如何记录前 i-1 个妓女的匹配方式便成了首要问题. 因为可选学生的人数n不多于13, 所以可以利用二进制的特性做压缩记录:
比如:
00000000 表示1个人都没选
00010000 表示选择了第5个学生妹( 从右开始数 )
00010010 表示选择了第5和第2个学生妹( 从右开始数 )
所以, Dp[ i ][ status ]表示前 i 个妓女以status方式选学生妹时的最优相似度, 其中, status二进制中1的数量必然为 i .
树状数组中介绍过用" lowBit( x ) = x & (-x) "的方式来巧妙地提取出x的二进制中最低位的1 .
相似地, " x & ~lowBit(x)"的方式可以巧妙地去除x的二进制中最低位的1
于是, 对于status, 我们可以交替采用上述方法来遍历其中所有的1, 或依次地单独剔除status中的每个1
那么:
Dp[ i ][ status ] = max( Dp[ i-1 ][ 单独剔除1的status ] + Like[ i -1 ][ 提取出来的那个1表示第几个学生妹 ] );
比如(以下status以二进制表示) :
Dp[ 3 ][ 0111 ] =Max {
Dp[2][ 0110 ] + Like[ 2][ 0] ,
Dp[2][ 0101 ] + Like[ 2][ 1],
Dp[2][ 0011 ] + Like[ 2][ 2],
}
单说的确麻烦, 直接上代码:
#include
#include
#include
#include
using namespace std;
#define MAXN 14
typedef vector Vector;
int Like[MAXN][MAXN]={0};
int dp[2][1<>=1);
return cnt;
}
// 获取最低位的1
// 复杂度O( logN )
inline int lowBit( int n ){ return n&(-n); }
// 获取最低位的0 的下标
// 复杂度O( logN )
int lowBitPos( int n ){
int cnt = 0;
while( n & 1 ){
++cnt;
n >>= 1;
}
return cnt;
}
int main(int argc, char** argv) {
scanf("%d",&N);
// 集合V[i]表示 : 二进制下1的数量为i的数的集合
// 复杂度O( 2^N ) * O( logN )
for( int i = 0, End=(1<