Aizu - 0525

Aizu - 0525

子集枚举 DFS

相似题目 飞行员兄弟 翻硬币 费解的开关

这种类型的题目,有个典型的特征,翻偶数次等于没翻,只有翻奇数次才有效果

即,要么翻,要么别翻,且操作之间是相互独立的,无序的

这个题有两个操作,翻行,或者翻列,我们枚举操作少的哪一个,即枚举行操作用二进制来枚举

然后对于每次的枚举结果,再进行列操作,而列并不需要真正的去翻,只需要统计那个多就行了

因为只有两种状态,且状态之间是会相互转化的,所以直接统计,然后累计就是答案

枚举每一种状态的时候记得要备份数组,因为你不能把原状态破坏了

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e4 + 10;
int g[20][N],backup[20][N],ans,t,r,c;
void turn_r(int x) {// 翻第 x 行所有的数
    for(int i = 0;i < c; ++i) {
        if(g[x][i] == 0) g[x][i] = 1;
        else g[x][i] = 0;
    }
}
int main() {
    while(scanf("%d%d",&r,&c) && r && c) {
        ans = 0;
        for(int i = 0;i < r; ++i)
            for(int j = 0;j < c; ++j)
                scanf("%d",&g[i][j]);
        for(int i = 0;i < 1 << r; ++i) {
            t = 0;
            memcpy(backup,g,sizeof g);
            for(int j = 0;j < r; ++j) {
                if(i >> j & 1) turn_r(j);// 翻这一行
            }
            for(int j = 0;j < c; ++j) {// 按列来统计
                int w = 0,b = 0;
                for(int k = 0;k < r; ++k) {
                    if(g[k][j] == 1) b ++;
                    else w ++;
                }
                t += max(w,b);
            }
            ans = max(t,ans);
            memcpy(g,backup,sizeof g);
        }
        printf("%d\n",ans);
    }
    return 0;
}
posted @ 2020-03-09 19:33  南风--  阅读(108)  评论(0编辑  收藏  举报