おせんべい
問題
IOI製菓では,創業以来の伝統の製法で煎餅(せんべい)を焼いている.この伝統の製法は,炭火で一定時間表側を焼き,表側が焼けると裏返して,炭火で一定時間裏側を焼くというものである.この伝統を守りつつ,煎餅を機械で焼いている.この機械は縦 R (1 ≤ R ≤ 10) 行, 横 C (1 ≤ C ≤ 10000) 列の長方形状に煎餅を並べて焼く.通常は自動運転で,表側が焼けたら一斉に煎餅を裏返し裏側を焼く.
ある日,煎餅を焼いていると,煎餅を裏返す直前に地震が起こり何枚かの煎餅が裏返ってしまった.幸いなことに炭火の状態は適切なままであったが,これ以上表側を焼くと創業以来の伝統で定められている焼き時間を超えてしまい,煎餅の表側が焼けすぎて商品として出荷できなくなる.そこで,急いで機械をマニュアル操作に変更し,まだ裏返っていない煎餅だけを裏返そうとした.この機械は,横の行を何行か同時に裏返したり縦の列を何列か同時に裏返したりすることはできるが,残念なことに,煎餅を1枚ごと裏返すことはできない.
裏返すのに時間がかかると,地震で裏返らなかった煎餅の表側が焼けすぎて商品として出荷できなくなるので,横の何行かを同時に1回裏返し,引き続き,縦の何列かを同時に1回裏返して,表側を焼きすぎずに両面を焼くことのできる煎餅,つまり,「出荷できる煎餅」の枚数をなるべく多くすることにした.横の行を1行も裏返さない,あるいは,縦の列を1列も裏返さない場合も考えることにする.出荷できる煎餅の枚数の最大値を出力するプログラムを書きなさい.
地震の直後に,煎餅が次の図のような状態になったとする.黒い丸が表側が焼ける状態を,白い丸が裏側が焼ける状態を表している.
1行目を裏返すと次の図のような状態になる.
さらに, 1列目と5列目を裏返すと次の図のような状態になる.この状態では,出荷できる煎餅は9枚である.
ヒント
R の上限 10 は C の上限 10000 に比べて小さいことに注意せよ.
入力
入力は複数のデータセットからなる.各データセットは以下の形式で与えられる.
入力の1行目には2つの整数 R, C (1 ≤ R ≤ 10, 1 ≤ C ≤ 10 000) が空白を区切りとして書かれている.続く R 行は地震直後の煎餅の状態を表す. (i+1) 行目 (1 ≤ i ≤ R) には, C 個の整数 ai,1, ai,2, ……, ai,C が空白を区切りとして書かれており, ai,j は i 行 j 列 の煎餅の状態を表している. ai,j が 1 なら表側が焼けることを, 0 なら裏側が焼けることを表す.
C, R がともに 0 のとき入力の終了を示す. データセットの数は 5 を超えない.
出力
データセットごとに,出荷できる煎餅の最大枚数を1行に出力する.
入出力例
入力例
2 5 0 1 0 1 0 1 0 0 0 1 3 6 1 0 0 0 1 0 1 1 1 0 1 0 1 0 1 1 0 1 0 0
出力例
9 15
上記問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。
题意是个出一个r行c列的矩阵由0和1组成,要求每次只能把一行或者一列取反,要求通过一系列操作之后,使1最多是多少。
显然每一行每一列,要么取反,要么不取反,也就是说取反次数<=1,如果取反两次就等于不取反,最好的答案肯定是某几行某几列取反,因此就可以枚举,当然不能枚举列,最多有10000列,是枚举不过来,行的范围和列的范围相差悬殊,最多有10行,这样直接枚举行是否取反就可以了,美剧到最后一行之后,再排着每一列去数最多的1,对于一列如果他的1比0多,显然不需要取反,如果0多,那么需要取反,也就是说最后算总和只需要记录0和1的个数的较大者即可。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <algorithm> #define MAX 10001 #define inf 0x3f3f3f3f using namespace std; int r,c,mp[MAX],ans; void reverse_(int k) { for(int i = 0;i < c;i ++) { mp[i] = ~(mp[i] ^ ~(1 << k));///把第k位取反 } } void dfs(int k) { if(k == r) { int t = 0; for(int i = 0;i < c;i ++) { int d = 0; for(int j = 0;j < r;j ++) { if(mp[i] >> j & 1)d ++; } t += max(d,r - d); } ans = max(ans,t); return; } dfs(k + 1); reverse_(k); dfs(k + 1); } int main() { int d; while(~scanf("%d%d",&r,&c) && (r + c)) { memset(mp,0,sizeof(mp));///初始mp数组为0 ans = 0; for(int i = 0;i < r;i ++) { for(int j = 0;j < c;j ++) { scanf("%d",&d); mp[j] ^= d << i;///通过位运算来实现,节省空间 } } dfs(0); printf("%d ",ans); } return 0; }