蓝桥杯——(状压二进制记忆化)金陵十三钗

题目


OJ平台

题目详解

  • 很明显,这道题也很快能想到暴力解法,我们只需要每一层进行选择枚举并且进行标记即可。

记忆化简述:
但我们发现会超时,这样的复杂度时指数级别的,我们很快想到可以记忆化,其实记忆化的过程就是把会重复的状态进行存储,比如一个最简单的记忆化也就是fib问题,它每次的状态到达f(i)肯定是重复的毫无疑问,再复杂一点,数位 dp 问题计算 0~N 的某个数字次数,我们是用到达该位置的次数来确定一个状态,因为一旦到达某个数字位计算的次数相同则下面的选择都是一样的了。

记忆化和dp的差别:很明显记忆化侧重于把重复的问题解决,而dp则是侧重于状态的转移,虽然很多时候两者可以相互转化。

此题的二进制记忆化:
首先如果我们直接暴力肯定是需要标记然后枚举的,也就是每一层的选择肯定是不同的,所以直接通过处于哪一层无法达到记忆化的效果,那么如何设置状态可以取分选择重复的情况呢?我们发现,只要知道前面哪些已经被选择过,那么后面的选择都是确定的,也就是后面的选择都是相同的,那么我们如何记录前面是否被选择(和先后顺序无关)呢?很简单,直接二进制进行bitmap记录然后用它来记录状态即可。也就是 memo[path] = ... 记录这个状态的答案,当然bitmap只是空间和时间最简单的选择,我们也可以用字符串来进行,但这样过程会变慢,但空间可以节省很多。

解题代码

#include <bits/stdc++.h>
using namespace std;
#define MAXN 14
int N;
int vec[MAXN][MAXN];
bool check[MAXN];
int memo[2<<MAXN];//用二进制枚举路径进行记忆化
int dfs(int pos,int path){
    
    
    if(pos==N+1){
    
    
        return 0;
    }
    if(memo[path])
        return memo[path];
    int res = 0;
    for(int i=1;i<=N;i++){
    
    
        if(check[i])
            continue;
        check[i] = true;
        int t = dfs(pos+1,path|(1<<i))+vec[pos][i];
        check[i] = false;
        res = max(res,t);
    }
    return memo[path] = res;//记忆化
}
int main() {
    
    
    cin>>N;
    for(int i=1;i<=N;i++)
        for(int j=1;j<=N;j++)
            cin>>vec[i][j];
    cout<<dfs(1,0);;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_50945504/article/details/121184598