题意:给定一个n∗m的矩阵,标记出其中的局部极小值,要求填入1…n∗m,求方案数
这题我刚看还以为是搜索之类的。。后来看见数据范围才发现是DP。。没想到要用容斥。
切了一下午,题解都没听。。上去讲了一下自己的想法就没听了。。当时感觉快改出来了。。结果吗。。赫赫,因为没有清零而导致tle我是绝对不会告诉你们滴~~
推荐一下PO姐的解法,讲的很明了,,这里也会有部分借鉴==
http://blog.csdn.net/popoqqq/article/details/48028773
根据数据,我们发现n*m最大只有28,那么,局部最小值(也就是’X’)最多也就有8个
那么,我们很容易想到状态压缩DP。
设F[I,S]表示已经填充了i个格子,局部极小值的状态为S(二进制)
预处理出cntj表示填充状态为j时共有多少位置是可以填充的(包括已填充的局部极小值位置)
那么有DP方程fi,j=fi−1,j∗C1cntj−i+1+∑k∈jfi−1,j−{k}
但是问题是这样虽然保证了标记的位置都是局部最小值,但是可能会导致一些未标记的位置成为局部极小值,因此我们枚举其他可以成为局部极小值的位置,容斥一下即可。
代码(pas):
const big=12345678;
const dx:array[1..8,1..2]of longint=((-1,-1),(-1,0),(-1,1),(0,1),(1,1),(1,0),(1,-1),(0,-1));
var
num,a,d:array[0..8,0..10]of longint;
f:array[0..30,0..1000]of longint;
ch:char;
boo:boolean;
xx,yy:array[0..10]of longint;
tot:array[0..1000]of longint;
cas,bool,x,y,i,j,ans,n,m,k,fuckyouman:longint;
function work:longint;
var
x,y,bool,cnt,i,j,p,s,k:longint;
begin
cnt:=0;
for i:=1 to n do
for j:=1 to m do
if (a[i,j]<>0)then
begin
xx[cnt]:=i;
yy[cnt]:=j;
inc(cnt);
end;
for s:=0 to (1<<cnt)-1 do
begin
tot[s]:=0;
fillchar(num,sizeof(num),0);
for i:=0 to cnt-1 do
if (s and(1<<i))<>0then
num[xx[i],yy[i]]:=1;
for i:=1 to n do
for j:=1 to m do
if (num[i,j]=0)then
begin
bool:=1;
for k:=1 to 8 do
if bool<>0 then
begin
x:=i+dx[k,1];
y:=j+dx[k,2];
if (x<1)or(x>n)or(y<1)or(y>m)then continue;
if (num[x,y]<>0)then bool:=0;
end;
if (bool=1)then
begin
inc(tot[s]);
//writeln(tot[s]);
end;
end;
end;
fillchar(f,sizeof(f),0);
f[0,0]:=1;
for i:=1 to n*m do
begin
for s:=0 to (1<<cnt)-1 do
if (f[i-1,s]<>0)then
begin
p:=0;
for j:=0 to cnt-1 do
if (s and (1<<j))=0 then
begin
p:=p or(1<<j);
f[i,s or(1<<j)]:=(f[i,s or(1<<j)]+f[i-1,s])mod big;
end;
f[i,s]:=(f[i,s]+f[i-1,s]*(tot[p]-(i-1)) mod big)mod big;
//writeln(f[i,s]);
end;
end;
exit(f[n*m,(1<<cnt)-1]);
end;
function get(x:longint):longint;
begin
if x and 1<>0 then exit(-1)
else exit(1);
end;
procedure tr(x,y,t:longint);
var
i,j,ret,k:longint;
begin
a[x,y]:=1;
for k:=1 to 8 do
begin
i:=x+dx[k,1];
j:=y+dx[k,2];
if (i<1)or(i>n)or(j<1)or(j>m)then continue;
if (d[i,j]<>0)then continue;
d[i,j]:=t;
end;
ret:=work;
ans:=(ans+get(t)*ret)mod big;
for j:=y+1 to m do
if (d[x,j]=0)then tr(x,j,t+1);
for i:=x+1 to n do
for j:=1 to m do
if (d[i,j]=0)then tr(i,j,t+1);
a[x,y]:=0;
for k:=1 to 8 do
begin
i:=x+dx[k,1];
j:=y+dx[k,2];
if (i<1)or(i>n)or(j<1)or(j>m)then continue;
if (d[i,j]=t)then d[i,j]:=0;
end;
end;
begin
readln(cas);
while cas<>0 do
begin
readln(n,m);
fillchar(a,sizeof(a),0);
fuckyouman:=0;
for i:=1 to n do
begin
for j:=1 to m do
begin
read(ch);
if ch='X' then a[i,j]:=1 else
begin
a[i,j]:=0;
inc(fuckyouman);
end;
end;
readln;
end;
if (fuckyouman=n*m)then
begin
writeln(0);
dec(cas);
continue;
end;
fillchar(d,sizeof(d),0);
bool:=1;
for i:=1 to n do
if bool<>0 then
begin
for j:=1 to m do
if (bool<>0)and(a[i,j]=1) then
begin
if (d[i,j]=-1)then bool:=0;
d[i,j]:=-1;
for k:=1 to 8 do
begin
x:=i+dx[k,1];
y:=j+dx[k,2];
if (x<1)or(x>n)or(y<1)or(y>m)then continue;
d[x,y]:=-1;
end;
end;
end;
if fuckyouman=n*m then bool:=0;
if (bool=0)then
begin
writeln(0);
dec(cas);
continue;
end;
ans:=work;
for i:=1 to n do
for j:=1 to m do
if (d[i,j]=0)then
begin
tr(i,j,1);
end;
writeln((ans+big)mod big);
dec(cas);
end;
end.