CQOI2012局部极小值 BZOJ2669 状压DP+容斥

168 篇文章 0 订阅
11 篇文章 0 订阅

题意:给定一个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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值