牛客练习赛58 矩阵消除游戏(贪心 01串枚举)

题目:https://ac.nowcoder.com/acm/contest/4090/C
https://ac.nowcoder.com/acm/problem/blogs/200190
雨巨的一篇题解,非常详细有用了

这个题目是一个贪心不过它比一般的贪心稍微复杂一点,这个题目看到它的数据范围比较小,可以用暴力点的方法。

首先一个知识点是:01串枚举
所谓01串枚举,就是我们在每个个体都面对两种选择的时候,可以用一个01串表示
这个在状压dp中也有类似的地方,总之就是用一个数的二进制表示状态,1为选,0位不选
然后还有另一条重要的性质:所有长度小于n的01串,转成十进制之后就是所有小于2^n的数字
这一点很好理解,比如说长度为6的二进制最大也就是111111,转化为十进制为63,因此【0,63】这个范围转化为二进制就可以表示长度为6是的所有选择情况。

知道了这一点就很好继续接下来的贪心了,我们只需在循环中改变列的选择情况,在列的选择固定下对于剩下的行进行从大到小排序贪心选择即可。

其他的一些知识点:__builtin_popcount()
GCC有一个叫做__builtin_popcount的内建函数,它可以精确的计算二进制1的个数。尽管如此,不同于__builtin_ctz,它并没有被 翻译成一个硬件指令(至少在x86上不是)。相反的,它使用一张类似上面提到的基于表的方法来进行位搜索。这无疑很高效并且非常方便。

位运算
x<<n,相当于将x左移n位,每左移一样扩大两倍,相当于x*2^n,右移则相反
&用于取0,|用于置1
单片机中经常用到位运算。

下面放代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f;
const int N=20;
using namespace std;
typedef long long ll;
int a[N][N];
int s[N];

bool cmp(int x,int y){
    
    
    return x>y;
}

int main ()
{
    
    
    //freopen("D:\\input.txt", "r", stdin);
    //freopen("D:\\output.txt", "w", stdout);
    int n,m,k,ans,res;
    cin>>n>>m>>k;
    k=min(k,min(n,m));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cin>>a[i][j];
    ans=0;
    for(int i=0;i<(1<<m);i++){
    
    //2的m次方-1的二进制代表所有被选的情况
        memset(s,0,sizeof(s));//每次对选后每一行的sum和清零
        res=0;
        int tol=__builtin_popcount(i);//二进制有多少个1
        if(tol>k)continue;
        for(int j=1;j<=n;j++){
    
    
            for(int k=0;k<m;k++){
    
    
                if((1<<k)&i)//判断具体的第j行上的每一个是否被选
                    res+=a[j][k+1];
                else
                    s[j]+=a[j][k+1];
            }
        }
        sort(s+1,s+n+1,cmp);//剩下的行从大到小排序
        for(int j=1;j<=k-tol;j++)
            res+=s[j];
        ans=max(ans,res);
    }
    cout<<ans<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/u011612364/article/details/104620978