牛客小白月赛23 A(子集生成 + 子集前缀和)

A
在这里插入图片描述
思路:
因为行的范围比较小,列的范围比较大,所以可以 2 n 2^n 枚举行的选择方式,然后在判断是否可行。复杂度 O ( 2 n m ) O(2^n\cdot m)
数据太水了

char w[50][N];
int x[N],y[N];
int _x[N],_y[N];
int main(){
    int t = read();
    while(t--){
        int n = read(),m = read(),a = read(),b = read();
        int S = (1<<n); 
        rep(i,1,n){
            rep(i,1,m){
                w[i][j] = gc();
                if(w[i][j] == '*') x[i] ++,y[j] ++;
            }
        }
        bool r = 1;
        rep(p,0,S-1){
            if(__builtin_popcount(p) != a) continue;
            rep(i,1,n) _x[i] = x[i];rep(i,1,m) _y[i] = y[i];
            rep(i,0,21){
                if((p>>i)&1) {
                    _x[i+1] = 0;
                    rep(j,1,m) if(w[i+1][j] == '*'&&_y[j]) _y[j] --;         
                }
            }
            int cnt = 0;
            rep(i,1,m) if(y[i]) cnt ++;
            if(cnt<=b) {r = 0;break;}
        }
        if(r) puts("yes");
        else puts("no");

    }
}  

O ( 2 n n + n m ) O(2^n\cdot n + n\cdot m) 做法
子集前缀和
因为行的范围很小,所以我们可以将每列的敌人压缩为一个二进制位,即 m n m\cdot n 预处理 每列的敌人状态 并统计每个状态的数目,如 n = 4 n=4 ,1000代表某一列的状态,第4行有一个敌人,并且 c n t [ 1000 ] = 2 cnt[1000]=2 ,说明这样的列有两个,那么我们消灭第四行,就会有两列敌人的个数变为0,那么对于 1001 = 3 1001=3 是否也如上面例子所说呢,不是的,因为 1001 1001 包含了 1000 , 0001 1000,0001 这两种情况,也就是说 c n t [ 1001 ] = c n t [ 1001 ] + c n t [ 1000 ] + c n t [ 0001 ] cnt[1001]=cnt[1001]+cnt[1000]+cnt[0001] 。这也就是预处理 自己前缀和的目的。
预处理完后,我们遍历 i : ( 1 < < n ) 1 i:(1<<n )-1 这些状态,如果 i i 中1的个数小于等于 a a 并且 m c n t [ i ] < = b m-cnt[i]<=b 就说明这个可以消除所有列。
很棒

char w[22][N];
unordered_map<int,int> cnt;
int main(){
    int t = read();
    while(t--){
        cnt.clear();
        int n = read(),m = read(),a = read(),b = read();
        rep(i,1,n){
            rep(j,1,m){
                cin >> w[i][j];
            }
        }
        rep(i,1,m){
            int p = 0;
            rep(j,1,n){
                if(w[j][i] == '*') p|= 1<<(j-1);
            }
            cnt[p] ++;
        }
        for(int i = 1;i <= n;++i){
            for(int j = 0;j < (1<<n);++j)
            if(((j>>(i-1))&1) == 0) cnt[j|(1<<(i-1))] += cnt[j];
        }
        bool r = 1;
        rep(i,0,(1<<n)-1){
            if(__builtin_popcount(i)<=a&&m-cnt[i]<= b) {r = 0;break;}
        }
        if(r == 0) puts("yes");
        else puts("no");
    }
}

发布了636 篇原创文章 · 获赞 38 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_43408238/article/details/105124753