• 状态压缩----方格取数


    状态压缩--方格取数

    标签: ACM


    题目:

    给你一个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

    解题思路:

    • 找出每一行的状态的状态用二进制保存,0表示该位置不取,1表示取
    • 将每一行所有状态的和保存到数组里面
    • 从第二行开始找该行与上一行不冲突的所有状态,保存状态最大之和, 一直找到最后一行为止
    • 遍历最后一行所有状态数值之和,最大值即为答案

    需要用到异或,感觉在状态压缩里面特别重要

    (1)按位与运算符(&)

    按位与运算将两个运算分量的对应位按位遵照以下规则进行计算:

    0 & 0 = 0, 0 & 1 = 0, 1 & 0 = 0, 1 & 1 = 1。
    

    即同为 1 的位,结果为 1,否则结果为 0。

    例如,设3的内部表示为011
    5的内部表示为101
    3&5的结果为001

    (2)按位或运算符(|)

    按位或运算将两个运算分量的对应位按位遵照以下规则进行计算:

     0 | 0 = 0, 0 | 1 = 1, 1 | 0 = 1, 1 | 1 = 1
    

    即只要有1个是1的位,结果为1,否则为0。

    (3)按位异或运算符(^)

    按位异或运算将两个运算分量的对应位按位遵照以下规则进行计算:

     0 ^ 0 = 0, 0 ^ 1 = 1, 1 ^ 0 = 1, 1 ^ 1 = 0
    

    即相应位的值相同的,结果为 0,不相同的结果为 1。
    例如,013^035结果为026
    异或运算的意思是求两个运算分量相应位值是否相异,相异的为1,相同的为0。按位异或运算的典型用法是求一个位串信息的某几位信息的反。如欲求整型变量j的最右4位信息的反,用逻辑异或运算017^j,就能求得j最右4位的信息的反,即原来为1的位,结果是0,原来为0的位,结果是1。

    AC代码

    #include <iostream>
    #include <string.h>
    #define M 17720      //每一行有最大17711种状态
    #define N 21
    using namespace std;
    int map[N][N];     //储存输入的数字
    int sum[N][M];  //第i行第j种状态的总和
    int state[M];
    int dp[N][M];
    int n,p;
    int max(int a,int b)    // 求最大值
    {
        return (a>b?a:b);
    }
    bool checkLine(int i)       //检查该状态是否符合题意
    {
        return !(i&(i>>1));
    }
    bool checkTwoLine(int i,int j)  //检查该行与上一行是否符合题意
    {
        return !(i&j);
    }
    void init()
    {
        int i,j,k;
        p=0;
        memset(sum,0,sizeof(sum));
        for(i=0;i<(1<<n);i++)  //将n改为20测试获得每一行有17711种状态
            if(checkLine(i))
                state[p++]=i;
    
        for(i=0;i<n;i++)
            for(j=0;j<p;j++)
                for(k=0;k<n;k++)
                    if((state[j]>>k)&1)
                    sum[i][j]+=map[i][n-k-1];
    
    }
    void solve()
    {
    
        int i,j,k;
        memset(dp,0,sizeof(dp));
        for(i=0;i<p;i++)
            dp[0][i]=sum[0][i];
    
        for(i=1;i<n;i++) //遍历每一行
            for(j=0;j<p;j++)   //遍历该行的每一种状态
                for(k=0;k<p;k++)   //遍历上一行的每一种状态
                {
                    if(checkTwoLine(state[j],state[k]))
                        dp[i][j]=max(dp[i][j],sum[i][j]+dp[i-1][k]);
                }
        int ans=dp[n-1][0];
        for(i=1;i<p;i++)
        {
            if(ans<dp[n-1][i])
                ans=dp[n-1][i];
        }
        cout<<ans<<endl;
    
    
    }
    int main()
    {
    
        while (cin>>n)
        {
            int i,j;
            memset(map,0,sizeof(map));
            for(i=0;i<n;i++)
                for(j=0;j<n;j++)
                cin>>map[i][j];
            init();
    
            solve();
        }
        return 0;
    }
    
  • 相关阅读:
    [LeetCode]题解(python):007-Reverse Integer
    [LeetCode]题解(python):006-ZigZag Conversion
    [LeetCode]题解:005-Longest Palindromic Substring优化
    [LeetCode]题解(python):005-Longest Palindromic Substring
    [LeetCode]题解(python):003-Longest Substring Without Repeating Characters
    [LeetCode]题解(python):002-Add Two Numbers
    [LeetCode]题解(python):001-Two-Sum
    【BZOJ1005】【HNOI2008】明明的烦恼
    BZOJ平推计划
    【BZOJ1004】【HNOI20008】cards
  • 原文地址:https://www.cnblogs.com/allen-lzl/p/7928737.html
Copyright © 2020-2023  润新知