https://www.luogu.org/problem/show?pid=2704
题意:
炮兵在地图上的摆放位子只能在平地('P')
炮兵可以攻击上下左右各两格的格子;
而高原('H')上炮兵能够攻击到但是不能摆放
求最多能摆放的炮兵的数量
就是这个意思。
难度提高,弱省省选
一开始是想写dfs(迷之八皇后)的,
但是看到数据量100就想dp了;
因为题目n的范围给的很少n<=10,想到状压
非常明显是一个状态压缩的dp(状压dp)
其实可以当做状压的入门题目来做。
由于本行的状态是由前若干行推出来的,所以想到了从上推到下(而不是从左推到右)
还有,本行的状态与前1行,前2行的状态密切相关的(炮兵不互相攻击)
写出我们dp的状态:f[i,r1j]表示当前第i行状态为j,i-1行状态为r1的炮兵数
转移其实就是非常简单了:(r2就是前第2行的状态)
f[i,r1,j]:=max(f[i,r1,j],f[i-1,r2,r1]+sum[j]);
转移的条件:r1和r2不与H有一个重合;r1和r2不能被互相打到或达j
本题主要难在处理dp初始化的方面;
首先我们需要保证每一种拓展出来的炮兵排列的方式需要在该行中横向不互相攻击;
这种每行不攻击的序列总数小于等于60;
pd函数非常好写:
function pd(x:longint):boolean;//判断此时放的是否合法(附近两位不能有1) begin if (x and (x<<1))<>0 then exit(false); if (x and (x<<2))<>0 then exit(false); exit(true); end;
引入col[i]的二进制数组,表示第i行‘H’的分布 if s[j]='H' then col[i]:=col[i] or (1<<(j-1));
注意这里需要j-1,因为二进制是从2^0开始的。
注意初始化第0行所有地点都是高山,由于第0行可能会影响第2行;
特殊处理第一行:(st数组保存所有合法的序列,cnt为合法序列的个数,st标号1~cnt)
for i:=1 to cnt do begin if (col[1] and st[i])=0 then f[1,1,i]:=sum[i]; end;
所以,本题的代码简单,主要注意的是初始值和dp式不要写错(4维的转移,暴力)
uses math; var n,m,i,j,r1,r2,ans,cnt:longint; f:array[0..100,0..100,0..100]of longint; //f[i,j,k]表示当前第i行状态为j,i-1行状态为k的炮兵数 s:string; st,sum,col:array[0..100]of longint; function pd(x:longint):boolean;//判断此时放的是否合法(附近两位不能有1) begin if (x and (x<<1))<>0 then exit(false); if (x and (x<<2))<>0 then exit(false); exit(true); end; function getbit(x:longint):longint;//求出二进制数x中有几个1 var sum:longint; begin sum:=0; while x>0 do begin if (x and 1)=1 then inc(sum); x:=x>>1; end; exit(sum); end; procedure getdp(m:longint); var e,i:longint; begin e:=1<<m; cnt:=0; for i:=0 to e-1 do if pd(i) then begin inc(cnt); st[cnt]:=i; //保存此时的状态 sum[cnt]:=getbit(i); //炮兵的个数 end; end; begin readln(n,m); getdp(m); fillchar(col,sizeof(col),0); for i:=1 to m do col[0]:=col[0] or (1<<i); for i:=1 to n do begin readln(s); for j:=1 to m do if s[j]='H' then col[i]:=col[i] or (1<<(j-1)); end; //col[i]表示第i行高地的分布 fillchar(f,sizeof(f),255);//clear! for i:=1 to cnt do begin if (col[1] and st[i])=0 then f[1,1,i]:=sum[i]; end; for i:=2 to n do for j:=1 to cnt do //现在这行的状态是j if (col[i] and st[j])=0 then for r1:=1 to cnt do //上一行状态是r1 if (st[j] and st[r1])=0 then for r2:=1 to cnt do //上上行的状态是r2 if (st[j] and st[r2])=0 then if f[i-1,r2,r1]<>-1 then f[i,r1,j]:=max(f[i,r1,j],f[i-1,r2,r1]+sum[j]); ans:=0; for i:=1 to cnt do for j:=1 to cnt do ans:=max(ans,f[n,i,j]); writeln(ans); end.