BZOJ 2669 局部极小值 CQOI2012

#局部最小值
####**题目网址:**http://www.lydsy.com/JudgeOnline/problem.php?id=2669
##题目描述
有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。
给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。

##数据范围
1<= n n n<=4, 1<= m m m<=7

##题解
这一题,挺难的,经过了我几个小时的努力,我还是对了,O(∩_∩)O~~
动态规划,F[ i i i][ s s s]表示前 i i i个数已经填完了,位置为 X X X的点的填充情况为 s s s时( s s s为2进制),填数的方案有多少。预处理出 R e s t Rest Rest数组, R e s t s Rest_s Rests( s s s为2进制)表示位置为 X X X的点的填充情况为 s s s时,有多少个位置可以放数(包括位置为 X X X的点)。
状态转移方程显然为:
[外链图片转存中…(img-ly0djsXn-1650997478448)]
其中- i i i+1等于-( i i i-1)。

但是我们发现这样做会把不是局部最小值的位置也会当成局部最小值,于是我们算出所有把别的位置也当成 X X X的情况(不重复),对于每种情况跑一边动态规划。如果多选偶数个 X X X,则加到答案贡献上,如果是奇数个,则对答案的贡献为负数。

##Code(Pascal)

label 123;
CONST
    mo=12345678;
var
    wz:array[1..8,1..2] of longint=
    ((-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1));
    n,m,j,k,l,t,y,i,o,p,kww:longint;
    ch:array[0..5,0..8] of char;
    jk,rr:array[0..5,0..8] of longint;
    kx:array[0..30,1..2] of longint;
    bz:array[0..30] of boolean;
    m2:array[0..10] of longint;
    f:array[0..28,0..512] of int64;
    rest:array[0..512] of int64;
    lz:array[0..30] of longint;
    ans:int64;
function ok(a,b:longint):boolean;
    begin
        if (a>0) and (a<=n) and (b>0) and (b<=m) then exit(true)
        else exit(false);
    end;
function js:int64;
     var
         i,j,l,s,p:longint;
     begin
         for i:=0 to m2[k]*2-1 do
         begin
             rest[i]:=0;
             inc(kww);
             for j:=1 to k do
             if m2[j] and i=0 then
             begin
                 rr[kx[j,1],kx[j,2]]:=kww;
                 for l:=1 to 8 do
                 rr[kx[j,1]+wz[l,1],kx[j,2]+wz[l,2]]:=kww;
             end;
             for j:=1 to n do
             for l:=1 to m do
             if rr[j,l]<>kww then inc(rest[i]);
         end;
         if k=0 then
         begin
             js:=1;
             for l:=1 to n*m do
             js:=(js*l) mod mo;
             exit;
         end;
         for i:=1 to n*m do
         for s:=0 to m2[k]*2-1 do
         f[i,s]:=0;
         f[0,0]:=1;
         for i:=1 to n*m do
         for s:=0 to m2[k]*2-1 do
         if rest[s]-i+1>0 then
         begin
             f[i,s]:=f[i-1,s]*(rest[s]-i+1) mod mo;
             for l:=1 to k do
             if s and m2[l]>0 then
             f[i,s]:=(f[i,s]+f[i-1,s-m2[l]]) mod mo;
         end;
         js:=f[n*m,m2[k]*2-1];
     end;
procedure dg(o,kk,uu:longint);
    var
        i,j,l,pp,ss:longint;
    begin
        ans:=(ans+o*js+mo) mod mo;
        ss:=kk*m+uu;
        for i:=kk to n do
        for l:=1 to m do
        if i*m+l>=ss then
        if ch[i,l]='.' then
        begin
            pp:=0;
            for j:=1 to 8 do
            if ch[i+wz[j,1],l+wz[j,2]]='X' then
            begin
                inc(pp);
                break;
            end;
            if pp=0 then
            begin
                inc(k);
                kx[k,1]:=i;
                kx[k,2]:=l;
                ch[i,l]:='X';
                dg(0-o,i,l+1);
                dec(k);
                ch[i,l]:='.';
            end;
        end;
    end;
 
begin
    m2[1]:=1;
    for i:=2 to 10 do
    m2[i]:=m2[i-1]*2;
    k:=0;
    readln(n,m);
    for i:=1 to n do
    begin
        for l:=1 to m do
        begin
            read(ch[i,l]);
                if ch[i,l]='X' then
                begin
                    inc(k);
                    kx[k,1]:=i;
                    kx[k,2]:=l;
                end;
            end;
            readln;
        end;
        for i:=1 to n do
        for l:=1 to m do
        if ch[i,l]='X' then
        for j:=1 to 8 do
        if (ch[i+wz[j,1],l+wz[j,2]]='X') and ok(i+wz[j,1],l+wz[j,2]) then
        begin
            ans:=0;
            goto 123;
        end;
        ans:=0;
        if k<>0 then
        dg(1,1,1);
        123:writeln(ans);
end.
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值