AOJ0525 Osenbei

AOJ0525 Osenbei

問題

IOI製菓では,創業以来の伝統の製法で煎餅(せんべい)を焼いている.この伝統の製法は,炭火で一定時間表側を焼き,表側が焼けると裏返して,炭火で一定時間裏側を焼くというものである.この伝統を守りつつ,煎餅を機械で焼いている.この機械は縦 R (1 ≤ R ≤ 10) 行, 横 C (1 ≤ C ≤ 10000) 列の長方形状に煎餅を並べて焼く.通常は自動運転で,表側が焼けたら一斉に煎餅を裏返し裏側を焼く.

ある日,煎餅を焼いていると,煎餅を裏返す直前に地震が起こり何枚かの煎餅が裏返ってしまった.幸いなことに炭火の状態は適切なままであったが,これ以上表側を焼くと創業以来の伝統で定められている焼き時間を超えてしまい,煎餅の表側が焼けすぎて商品として出荷できなくなる.そこで,急いで機械をマニュアル操作に変更し,まだ裏返っていない煎餅だけを裏返そうとした.この機械は,横の行を何行か同時に裏返したり縦の列を何列か同時に裏返したりすることはできるが,残念なことに,煎餅を1枚ごと裏返すことはできない.

裏返すのに時間がかかると,地震で裏返らなかった煎餅の表側が焼けすぎて商品として出荷できなくなるので,横の何行かを同時に1回裏返し,引き続き,縦の何列かを同時に1回裏返して,表側を焼きすぎずに両面を焼くことのできる煎餅,つまり,「出荷できる煎餅」の枚数をなるべく多くすることにした.横の行を1行も裏返さない,あるいは,縦の列を1列も裏返さない場合も考えることにする.出荷できる煎餅の枚数の最大値を出力するプログラムを書きなさい.

地震の直後に,煎餅が次の図のような状態になったとする.黒い丸が表側が焼ける状態を,白い丸が裏側が焼ける状態を表している.

1行目を裏返すと次の図のような状態になる.
1行目を裏返すと次の図のような状態になる.
さらに, 1列目と5列目を裏返すと次の図のような状態になる.この状態では,出荷できる煎餅は9枚である.
さらに, 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行に出力する.

入出力例

入力例

1
2
3
4
5
6
7
8
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

出力例

1
2
9
15

答え

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <cstdio>
#include <algorithm>
#include <bitset>
using namespace std;

int R, C, ans = 0;
bitset<10000> a[10];

void dfs(int k) {
if (k == R) {
int tmp = 0;
for (int j = 0; j < C; ++j) {
int num = 0;
for (int i = 0; i < R; ++i) {
if (a[i][j]) ++num;
}
tmp += max(num, R -num);
}
ans = max(ans, tmp);
return;
}
dfs(k+1);
a[k].flip();
dfs(k+1);
}

int main() {
while (true) {
scanf("%d %d", &R, &C);
if (R == C && R == 0)
break;
for (int i = 0; i < R; ++i) {
for (int j = 0; j < C; ++j) {
int x;
scanf("%d", &x);
a[i][j] = x;
}
}
ans = 0;
dfs(1);
printf("%d\n", ans);
}
return 0;
}

分析

  1. 有一个 0 和 1 构成的矩阵,每次可以翻转一行或者一列,求矩阵经过若干次翻转后,1 的最大个数。
  2. 最多 10 行,可对行的翻转情况进行枚举,或者 dfs。
  3. 每种行的翻转情况,对于列,只需求出列中 0 和 1 分别的个数,取其大值即可(这就是列翻转的目的,求一列 1 的最多个数),无需遍历列的翻转情况。
  4. biset 的使用。
# AOJ

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×