Toy Bricks
【题目描述】
Ray又在NPC问题了:这里有一个箱子,还有若干个玩具。
我们可以假设玩具的数量是没有上限的。我们还知道这个箱子是不规则的,或者可以说,他的外形比较像一个矩形,但是内部却有很多突起,这些突起很可恶,他可以一直突到玩具的顶盖部。为了简化这个NPC问题,Ray只保留了一种形状的玩具
○
○ ○ ○
○
Ray想知道对于一个给定的箱子,最多可以放下多少个这样的玩具。
【输入格式】
第一行两个用空格隔开的整数n 和 m 表示玩具想的长度和宽度
第二行到第n+1行,每行m个用空格隔开的字符‘#’’.‘;’#’表示这里是一个障碍物。’.’表示这里可以放东西。
【输出格式】
一行一个整数表示最多的玩具个数
【输入样例】
5 4
#.#.
…#
#..#
#...
##.#
【输出样例】
2
【数据范围】
0<=n<=100
0<=m<=10
状态压缩动规,参见炮兵阵地。
program lonely;
const
maxn=maxlongint shr 1;
var
tot,jj,kk,l,zz,ttttt,ss,i,j,n,m,k,t,temp,ans:longint;
b,sum:array[0..2000] of integer;
z:array[0..2000] of integer;
g:array[0..2000,0..2000] of integer;
f:array[0..1,0..2000,0..2000] of longint;
c:char;
begin
assign(input,'toy.in');
reset(input);
assign(output,'toy.out');
rewrite(output);
readln(n,m);
fillchar(sum,sizeof(sum),0);
for i:=0 to 1 shl m-1 do
begin
t:=i;
while t>0 do
begin
inc(sum[i]);
t:=t-(t and (-t));
end;
end; //最多能放多少个
fillchar(z,sizeof(z),0);
for i:=1 to n do
begin
for j:=0 to m-1 do
begin
read(c);
if c='#' then
z[i]:=z[i]+1 shl j;
end;
readln;
end; //设置障碍
tot:=0;
fillchar(g,sizeof(g),0);
fillchar(b,sizeof(b),0);
for i:=0 to 1 shl m-1 do
begin
if (i and 1=1)or(i shl 1>(1 shl m-1)) then continue;
if (i shl 1)and i<>0 then continue;
if (i shl 2)and i<>0 then continue; //不能放置
inc(tot);
b[tot]:=i;
g[i,0]:=0;
for j:=0 to 1 shl m-1 do
begin
if (j and 1=1)or(j shl 1>(1 shl m-1)) then continue;
if (i and j<>0) then continue;
if (j shl 1)and j<>0 then continue;
if (j shl 2)and j<>0 then continue;
if (i shl 1)and j<>0 then continue;
if (j shl 1)and i<>0 then continue;
inc(g[i,0]);
g[i,g[i,0]]:=j;
end;
end;
for i:=0 to 1 do
for j:=0 to 1025 do
for k:=0 to 1025 do
f[i,j,k]:=-maxn;
f[1,0,0]:=0;
t:=0;
for i:=2 to n-1 do
begin
for jj:=1 to tot do
for kk:=1 to tot do
begin
j:=b[jj];k:=b[kk];
if f[1-t,j,k]>=0 then
begin
for l:=1 to g[j,0] do
begin
temp:=g[j,l];
if temp and z[i]<>0 then continue;
if (temp shr 1)and(z[i])<>0 then continue;
if (temp shl 1)and(z[i])<>0 then continue;
if temp and z[i+1]<>0 then continue;
if temp and z[i-1]<>0 then continue;
if temp and k<>0 then continue;
ss:=sum[temp];
if f[1-t,j,k]+ss>f[t,temp,j] then
f[t,temp,j]:=f[1-t,j,k]+ss;
end;
f[1-t,j,k]:=-maxn;
end;
end;
t:=1-t;
end; //状态压缩动规。
ans:=0;
for i:=1 to tot do
for j:=1 to tot do
if f[1-t,b[i],b[j]]>ans then
ans:=f[1-t,b[i],b[j]]; //统计
writeln(ans);
close(input);
close(output);
End.
补充,解题报告 炮兵阵地
1. 题目 炮兵阵地
还是宇宙时间公元5.5亿年,maxingc联盟用微子来攻击yun联盟。愤怒的yun联盟决定反戈一击,他们准备使用加农炮来反击。
yun联盟的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
Sample Input
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
Sample Output
6
2. 题目实质
数据变态一点的,改了改要求的 n 皇后,不过是要求求 n 。
3. 题目来源 怎么又是集训?
4. 算法
虽然他跟 n 皇后怎么看怎么类似,但是从那该死的数据范围(N <= 100;M <= 10)。可以看出,用单纯的搜肯定过不了。
由于m<=10,所以每一行的状态可以用二进制表示,放置炮兵记为1,不放置记为0。每一行的状态只和前两行的状态有关系,所以有方程: F[i,k1,k2]=Max{f[i-1,k2,k3]+num[k1]},其中i为行数,k1表示第i行的状态,k2表示第i-1行的状态,k3表示第i-2行的状态,num[k1]为k1状态下可放置炮兵的个数。 i,k1,k2,k3必须满足一定的关系(炮兵之间不能相互攻击,且炮兵不能放在山地上)。 对于状态a[k1]和a[k2],如果他们是可行的,讨论他们每个对应的位置。 a[k1]如果某位置是1,a[k2]这位上必须是0。 a[k1]如果某位置是0,a[k2]这位上可以是1,也可以是0。 所以可以归纳出 a[k1] and a[k2]=0,这要判断矛盾可以使速度大大提高。
用这种 and 的方法可以应付很多变态的判断。 对于每一行,第i种状态能不能放上去,即要求不能在‘H’的地方放士兵。 我们可以先把每行原来的初始状态也表示出来,但是这里有个小技巧,把‘H’的地方记录下来。 这行如果某位置是1,那么在a[k1]中这个位置上必须为0(1代表这里是山地)。 这行如果某位置是0,那么在a[k1]中这个位置上可以为1,可以为0(0代表这里是平原)。 所以可以归纳出 now[i] and a[k1]=0(now数组表示地形的状态)。
当然,这种题,一般,大概,也许,都是第四题,所以拿裸宽搜偏偏分也没什么不好,不然这么变态的程序,想要在考试的时候敲出来真的是有点……
5. 注意事项
注意看数据范围,这玩意跟 n 皇后还真有点不一样。
这里的二进制优化也就是 HASH 优化,所以注意这关键的 HASH 函数要写对!
其实这个款搜有点动态规划的意思,但这个动态规划中的状态是要用款搜进行预处理的,千万别忘了。
还有这个判断矛盾的方法可以学一下。
6. 时空复杂度 O(n^2)
7. 程序代码
Pascal
var f:array[0..100,0..100,0..100] of longint;
now,b,a:array[0..100] of longint; ch:char;
i,j,n,m,sum,t,tt,ans,k1,k2,k3:longint; s:string;
function deal(x:longint):string;
var s:string; k:longint;
begin
k:=x; s:='';
while k>0 do
begin
s:=chr(k mod 2+48)+s;
k:=k div 2;
end;
exit(s);
end;
begin
assign(input,'cannon.in'); reset(input);
assign(output,'cannon.out'); rewrite(output);
readln(n,m);
for i:=1 to n do
begin
for j:=1 to m do
begin
read(ch);
if ch='H' then now[i]:=now[i]+1 shl (m-j);
end;
readln;
end;
for i:=0 to (1 shl m)-1 do
begin
s:=deal(i);
for j:=1 to m-length(s) do s:='0'+s;
tt:=0; t:=0;
for j:=1 to m do
if s[j]='1' then
begin
if (s[j+1]='1') and (j+1<=m) then t:=1;
if (s[j+2]='1') and (j+2<=m) then t:=1;
if t=1 then break;
inc(tt);
end;
if t=0 then
begin
inc(sum);
a[sum]:=i;
b[sum]:=tt;
end;
end;
for i:=1 to sum do
f[1,i,1]:=b[i];
for i:=2 to n do
for k1:=1 to sum do
for k2:=1 to sum do
if (a[k1] and a[k2]=0) and
(a[k1] and now[i]=0) and (a[k2] and now[i-1]=0) then
for k3:=1 to sum do
if (a[k2] and a[k3]=0) and (a[k3] and now[i-2]=0)
and (a[k1] and a[k3]=0) then
if f[i,k1,k2]<f[i-1,k2,k3]+b[k1] then
f[i,k1,k2]:=f[i-1,k2,k3]+b[k1];
for i:=1 to sum do
for j:=1 to sum do
if (a[i] and a[j]=0) and (a[i] and now[n]=0)
and (a[j] and now[n-1]=0) then
if f[n,i,j]>ans then ans:=f[n,i,j];
writeln(ans);
close(input);
close(output);
end.