• 状态压缩


    状态压缩通常就是对二进制而言的,在学习树状数组的时候,一个特别重要的东西 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
    ", 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
    ", ans);
        }
        return 0;
    }
    

      

    东北日出西边雨 道是无情却有情
  • 相关阅读:
    一些前台技巧
    javascript中的面向对象
    vs.net 常用快捷键
    js和C#中的进制转换方法
    MFC中CDC相关图形,文本的一些使用方法(转)
    poj1505
    poj1401
    poj2533
    poj1504
    poj1384
  • 原文地址:https://www.cnblogs.com/ccut-ry/p/8582625.html
Copyright © 2020-2023  润新知