CQOI2012 BZOJ2669 【NOIP2016提高A组模拟8.15】Garden

本文介绍了一个关于在一个N*M的数组中填充数字的算法问题,要求所有标记为X的位置及其八邻域的数字必须满足特定条件。文章详细阐述了针对不同数据规模的解决方案,包括状态压缩DP的基本思路及100%数据通过的优化方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//终于把这!@#¥%U!%@#^&@#()&&@%¥的题目切掉了

题目

这里写图片描述
Input
这里写图片描述
Sample Input

2
3 2
X.
..
.X
2 2
X.
..
Output
这里写图片描述
Sample Output

60
6
Data Constraint
这里写图片描述

题目大意

有一个N*M的数组,每一个位置有X和.两种,将1到n *m填入这个数组中,所有X,并且是只有X的八相邻位置都要比X这个位置的值大,求方案数。(如果有一个“.”的八相邻位置都比当前位置大,那么这种情况是不合法的)

比赛时の想法

好难啊不会(╯‵□′)╯

正解

对于40%,不难发现一组数据中X的数量最多不会超过8,那么我们考虑状压所有为X的的位置,定义rest数组为可以填数的位置(未标记)加已经填了数的位置(标记)的和(这些位置填了对后面的数没有影响,即没有后效性)。然后我们就可以状压dp了,dp的方程式是十分明显的。F[i][S]=F[i1][S](Rest[S]i+1)+psf[i1,sp]。(设f[i,s]表示正在为数字i选择对应的位置,X的状态为S)
对于100%的数据,现在的问题是可能会有一些点出现题目大意中描述的不合法的情况,这个时候我们容斥一下就可以了(把这样的”.”也当成”X”,然后在去跑状压dp,看一下程序里面就很容易懂了~)

贴代码

const md=12345678;
var
    f:array[0..30,0..300]of int64;
    p:array[0..300]of int64;
    c:array[0..10,0..10]of longint;
    s:array[0..10,0..10]of char;
    g:array[0..10]of int64;
    q:array[1..8,1..2]of longint=((1,1),(1,0),(1,-1),(0,1),(0,-1),(-1,1),(-1,0),(-1,-1));
    i,j,k,l,n,m,t,o:longint;
    ans:int64;
    ch:char;
function ps(x:int64):int64;inline;
begin
    if x=1 then exit(-1) else exit(1);
end;
function max(x,y:longint):longint;inline;
begin
    if x>y then exit(x) else exit(y);
end;
function getans:int64;inline;
var
    i,j,k,l,x,y,t1,t2:longint;
begin
    l:=0;
    for i:=1 to n do
        for j:=1 to m do
            if s[i,j]='X' then inc(l);
    fillchar(p,sizeof(p),0);
    for i:=0 to g[l+1]-1 do
    begin
        k:=0;
        fillchar(c,sizeof(c),0);
        for x:=1 to n do
            for y:=1 to m do
            begin
                if s[x,y]='.' then continue;
                inc(k);
                if i and g[k]=0 then
                begin
                    c[x,y]:=1;
                    for j:=1 to 8 do c[x+q[j,1],y+q[j,2]]:=1;
                end;
            end;
        for x:=1 to n do
            for y:=1 to m do if c[x,y]=0 then inc(p[i]);
    end;
    fillchar(f,sizeof(f),0);
    f[0,0]:=1;
    for i:=1 to n*m do
        for j:=0 to g[l+1]-1 do
        begin
            f[i,j]:=(f[i-1,j]*max(p[j]-i+1,0)) mod md;
            for k:=1 to l do
                if j and g[k]>0 then
                    f[i,j]:=(f[i,j]+f[i-1,j-g[k]]) mod md;
        end;
    exit(f[I,J]);
end;
procedure dfs(x,y,z:longint);inline;
var
    i,t1,t2:longint;
    bz:boolean;
begin
    if y=m+1 then
    begin
        y:=1;
        inc(x);
    end;
    if x=n+1 then
    begin
        ans:=(ans+z*getans+md) mod md;
     //   writeln(ans);
        exit;
    end;
    dfs(x,y+1,z);
    bz:=true;
    if s[x,y]='X' then bz:=false;
    if bz=true then
    for i:=1 to 8 do
    begin
        t1:=x+q[i,1];
        t2:=y+q[i,2];
        if s[t1,t2]='X' then
        begin
            bz:=false;
            break;
        end;
    end;
    if bz=true then
    begin
        s[x,y]:='X';
        dfs(x,y+1,ps(z));
        s[x,y]:='.';
    end;
end;
procedure qing;
begin
    for i:=1 to n do
        for j:=1 to m do s[i,j]:=' ';
end;
begin
    readln(o);
    g[1]:=1;
    for i:=2 to 10 do g[i]:=g[i-1]*2;
    for o:=1 to o do
    begin
        readln(n,m);
        for i:=1 to n do
        begin
            for j:=1 to m do read(s[i,j]);
            readln;
        end;
        ans:=0;
        l:=0;
        for i:=1 to n do
            for j:=1 to m do
            begin
                if s[i,j]='X' then
                begin
                    inc(l);
                    for k:=1 to 8 do
                        if s[i+q[k,1],j+q[k,2]]='X' then
                            ans:=-1;
                    if ans=-1 then break;
                end;
            end;
        if l=0 then ans:=-1;
        if ans=-1 then
        begin
            writeln(0);
            qing;
            continue;
        end;
        ans:=0;
        dfs(1,1,1);
        writeln(ans);
        qing;
    end;
end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值