状态压缩

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了状态压缩相关的知识,希望对你有一定的参考价值。

状态压缩通常就是对二进制而言的,在学习树状数组的时候,一个特别重要的东西 x & (-x) , 表示的含义是去掉 x 高位的 1 , 只保留最低位的 1 , 如果  x 在二进制下只有一个 1, 那这样得到的数就是它本身。

 

x ^ (x&-x) 表示的含义是去掉最低位的 1 个 1, 这个式子等价于 x - (x&-x)

 

来看一道装压的入门题 :

给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。Input包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)Output对于每个测试实例,输出可能取得的最大的和Sample Input

3
75 15 21 
75 15 28 
34 70 5 

Sample Output

188

题目分析 : 这题可以用装压水过去,首先我们要处理的是每行可以选定的状态有哪些,然后预处理出来这些状态下对应的花费总和
dp[i][j] 表示的含义是在第 i 行,选取 j 状态下的最大花费
在这里有 2 个判定条件要注意下,就是如何用二进制判断在不在同一行 x&(x>>1), 如何这个条件成立,则说明有两个 1 之间是相邻的,
判断同上一行的位置关系是否满足,只需要判断 x & y 是否等于 1 ,若等于1则说明上下两行有 1 相邻
代码示例 :
#define ll long long
const int maxn = 1e5+5;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;

int n;
int mp[25][25];
int dp[25][maxn]; // 第 i 行取第 j 个状态时的最大和
int sum[25][maxn]; // 第 i 行取第 j 个状态的花费
int s[maxn];
int p;

bool checkh(int x){
    return !(x&(x>>1));
}

bool checkl(int x, int y){
    return !(x&y);
}

void init() {
    p = 0;    
    for(int i = 0; i < (1<<n); i++){
        if (checkh(i)) s[p++] = i; 
    }
    //printf("p = %d\n", p);
    for(int i = 1; i <= n; i++) {
        for(int j = 0; j < p; j++){
            int state = s[j];
            int res = 0;
            int pos = n;
            while(state){
                if (state & 1) res += mp[i][pos];
                state >>= 1;
                pos--;
            }
            sum[i][j] = res;
        }
    }
    for(int i = 0; i < p; i++){
        dp[1][i] = sum[1][i];
    }
}

int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    while(~scanf("%d", &n)){
        memset(dp, 0, sizeof(dp)); // 初始化 !! 
        
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                scanf("%d", &mp[i][j]);        
            }
        }
        init();
        for(int i = 2; i <= n; i++){
            for(int j = 0; j < p; j++){ // 枚举当前行的全部状态
                for(int k = 0; k < p; k++) { // 枚举上一行的全部状态
                    if(checkl(s[j], s[k])) {
                        dp[i][j] = max(dp[i][j], dp[i-1][k]+sum[i][j]);
                    }
                }    
            }
        }
        int ans = dp[n][0];
        for(int i = 1; i < p; i++) ans = max(ans, dp[n][i]);
        printf("%d\n", ans);
    }
    return 0;
}

  

 

以上是关于状态压缩的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段13——Vue的状态大管家

java BottomBarNavigation代码保留片段状态

仅在一个片段中隐藏状态栏并在其他片段中显示

PostgreSQL 是不是支持表(片段)的透明压缩?

切换片段时如何维护子视图的状态?

使用 savedInstanceState 保存片段状态