print2. Комбинаторные объекты

printРазбор задачи 592. Головоломка

Переключать более одного раза столбец или строку нет смысла. Если будет выбрано некоторое подмножество столбцов, которые нужно переключить, тогда для каждой строки можно легко определить, стоит ли ее переключать. Если количество минусов в строке больше количества плюсов, то строку переключаем, иначе – не переключаем.
var b:array[1..20] of boolean; { флаги для переключений столбцов }
    n,i,mmin:integer;
    t:array[1..20] of string;
procedure gen(i:integer);
var j,k,s,sm:integer;
begin
  if i>n then
  begin
    { обработка полученного подмножества }
    sm:=0; { считаем число минусов в таблице }
    for j:=1 to n do
    begin
      s:=0; { считаем число минусов в строке в зависимости от переключений столбцов }
      for k:=1 to n do
        if b[k] xor (t[j][k]='-') then
          inc(s);
      if s>n-s then { количество минусов больше плюсов }
        s:=n-s; { переключаем }
      sm:=sm+s;
    end;
    if sm<mmin then { результат улучшился? }
      mmin:=sm;
    exit;
  end;
  b[i]:=false; 
  gen(i+1); 
  b[i]:=true; 
  gen(i+1); 
end;
begin
  readln(n);
  for i:=1 to n do
    readln(t[i]);
  mmin:=n*n; { минимальное количество минусов }
  gen(1);
  writeln(mmin);
end.
Время работы данного решения равно `O(N^2*2^N)`, и при `N=20` потребуется более `4*10^8` операций и, следовательно, программа не пройдет ограничения по времени. Чтобы ускорить вычисления, определение количества минусов можно сделать не при обработке подмножества, а в процессе рекурсии.
var b:array[1..20] of boolean; { флаги для переключений столбцов }
    n,i,mmin:integer;
    t:array[1..20] of string;
    s:array[1..20] of integer; { количество минусов в первых i столбцах }
procedure gen(i:integer);
var j,k,sm:integer;
    ss:array [1..20] of integer; { для сохранения массива s }
begin
  if i>n then
  begin
    { обработка полученного подмножества }
    sm:=0; { считаем число минусов в таблице }
    for j:=1 to n do
    begin
      if s[j]>n-s[j] then
        sm:=sm+n-s[j]
      else
        sm:=sm+s[j];
    end;
    if sm<mmin then { результат улучшился? }
      mmin:=sm;
    exit;
  end;
  for j:=1 to n do
  begin
    ss[j]:=s[j]; { сохраняем количество минусов }
    if t[j][i]='-' then
      inc(s[j]); 
  end;
  b[i]:=false;
  gen(i+1);
  for j:=1 to n do
  begin
    s[j]:=ss[j]; { восстанавливаем количество минусов }
    if t[j][i]='+' then
      inc(s[j]);
  end;
  b[i]:=true;
  gen(i+1);
  for j:=1 to n do
    s[j]:=ss[j]; { восстанавливаем количество минусов }
end;
begin
  readln(n);
  for i:=1 to n do
    readln(t[i]);
  mmin:=n*n; { минимальное количество минусов }
  gen(1);
  writeln(mmin);
end.
Время работы данного решения равно `O(N*2^N)`. Дополнительно уменьшить время работы можно, выключив проверку выхода за границы массива с помощью комментария {$R-} в начале программы.
loading