密码盘 (Standard IO)

Description

如图是某人设想中的N×N的密码盘,用以显示自己强大的智商以及计算能力。图中每列上面有一个0或1的值,每行左边也有一个0或1的值。密码盘中有最多N*N个按钮,每个按钮有一个数值。按钮按下去之后,你会获得按钮上的分数,然后对应行和对应列的值会改变。
例如:假设按钮(1,4)的数值为k,按下它,你获得k分,然后第一行的1会变成0,第四列的0会变成1。
你的任务是,使每列上面的值和每行左边的值一一对应相等(从左到右和从上到下),并且取得最大的积分。当然了初始积分为0。这里写图片描述

Input

第一行一个正整数N,表示密码盘的大小。N最大为9。
第二行一个01串,表示从上到下每行左边的N个值。
第三行一个01串,表示从左到右每列上边的N个值。
下一行一个正整数k,表示按钮的数量。1≤k≤N*N。
接下来k行,每行三个整数a、b、c,表示第a行第b列处有一个数值为c的按钮。左上角的格子为第一行第一列。1≤a≤N,1≤b≤N,-100≤C≤100。不会有同一个地方出现两个按钮。

Output

输出一行,包含一个整数,表示所能取得的最大积分。如果不能使得每列上面的值和每行左边的值一一对应相等,输出“I am stupid!”。

题解
状态压缩DP:
f[i][j][k]=max(f[u][v][k-1]+w[k],f[i][j][k-1])

代码

var
  n,m,nm,sum,l:longint;
  f:array [0..512,0..512,0..1] of longint;
  x,y,w:array [0..81] of longint;
function max(t,k:longint):longint;
begin
  if t>k then exit(t);
  exit(k);
end;

procedure init;
var
  i,j,t,k:longint;
  s:string;
begin
  readln(n);
  nm:=(1 shl n)-1;
  readln(s);
  t:=0;
  for i:=1 to n do
    t:=t+(ord(s[i])-48) shl (i-1);
  readln(s);
  k:=0;
  for i:=1 to n do
    k:=k+(ord(s[i])-48) shl (i-1);
  for i:=0 to nm do
    for j:=0 to nm do
      f[i,j,0]:=-maxlongint div 3;
  f[t,k,0]:=0;
  readln(m);
  for i:=1 to m do
    readln(x[i],y[i],w[i]);
end;

procedure main;
var
  i,j,k,v,u:longint;
begin
  l:=0;
  for k:=1 to m do
    begin
      l:=l xor 1;
      for i:=0 to nm do
        for j:=0 to nm do
          begin
            u:=i xor (1 shl (x[k]-1));
            v:=j xor (1 shl (y[k]-1));
            f[i,j,l]:=max(f[i,j,l xor 1],f[u,v,l xor 1]+w[k]);
          end;
    end;
end;

procedure print;
var
  i:longint;
begin
  sum:=-maxlongint div 4;
  for i:=0 to nm do
    sum:=max(sum,f[i,i,l]);
  if sum<>-maxlongint div 4 then write(sum)
                            else write('I am stupid!');
end;

begin
  init;
  main;
  print;
end.
原文地址:https://www.cnblogs.com/zyx-crying/p/9319590.html