BZOJ 2669 局部极小值&JZOJ.4700 Garden

Problem

本题出自CQOI2012,BZOJ2669。
题目网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2669

Description

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

Sample Input

2
3 2
X.
..
.X
2 2
X.
..

Sample Output

60
6

Data Constraint

这里写图片描述

Solution

我们可以发现,矩阵里最多出现8个’X’,所以我们可以状压DP。
首先预处理一个Rest数组,Rest[s] (s是2进制数)表示位置为X的点的填充状态为s时,可以放数的位置个数(包括位置为X的点)。
那么我们可以得出DP方程:(k比s少一位2进制位)

F[i][s]=ΣksF[i1][k]+F[i1][s](Rest[i](i1))
.
但是,我们发现这样一种情况:
这里写图片描述
所以,如果有出现一种是’.’的格子,且周围全是’.’,那么我们要暴力,适当的将这些’.’改为’X’.求出所有当X有奇数个的时候加上DP后的结果;偶数个的时候减去DP后的结果。
注意特殊情况特判。

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define N 10
#define mo 12345678
#define fo(i,a,b) for(i=a;i<=b;i++)
const int fx[8][2]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
int _,n,m,i,j,k,l,od,s;
int _2[12],a[N][N],b[30][2],bz[N][N],f[N*N][1000],rest[1000];
char c[N][N];
long long ans;
int dp()
{
    int tot=0,S=0;
    fo(i,1,n)
        fo(j,1,m)
            if (c[i][j]=='X')
            {
                S+=_2[++tot];
                b[tot][0]=i;
                b[tot][1]=j;
            }
    fo(i,0,S)
    {
        od++;
        rest[i]=0;
        fo(j,1,tot)
            if ((_2[j]&i)==0)
            {
                bz[b[j][0]][b[j][1]]=od;
                fo(k,0,7) bz[b[j][0]+fx[k][0]][b[j][1]+fx[k][1]]=od;
            }
        fo(j,1,n)
            fo(k,1,m)
                if (bz[j][k]!=od) rest[i]++;
    }
    memset(f,0,sizeof(f));
    f[0][0]=1;
    fo(i,1,n*m)
        fo(s,0,S)
        {
            f[i][s]=(f[i][s]+f[i-1][s]*(rest[s]-i+1)%mo)%mo;
            fo(j,1,tot)
                if ((_2[j]&s)>0)
                    f[i][s]=(f[i][s]+f[i-1][s-_2[j]])%mo;
        }
    return f[n*m][_2[tot+1]-1];
}
void dg(int x,int y,int z)
{
    int sum;
    if (x>n)
    {
        sum=dp()*z;
        ans=(ans+sum+mo)%mo;
        return;
    }
    if (y==m) dg(x+1,1,z);else dg(x,y+1,z);
    bool p=0;
    int xx,yy;
    fo(i,0,7)
    {
        xx=x+fx[i][0];
        yy=y+fx[i][1];
        if (c[xx][yy]=='X')
        {
            p=1;
            break;
        }
    }
    if (!p && c[x][y]!='X')
    {
        c[x][y]='X';
        if (y==m) dg(x+1,1,-z);else dg(x,y+1,-z);
        c[x][y]='.';
    }
}
int main()
{
    _2[1]=1;
    fo(i,2,10) _2[i]=_2[i-1]*2;
    scanf("%d",&_);
    while (_--)
    {
        memset(a,127,sizeof(a));
        memset(c,0,sizeof(c));
        scanf("%d%d",&n,&m);
        scanf("\n");
        l=0;
        fo(i,1,n)
        {
            fo(j,1,m) 
            {
                scanf("%c",&c[i][j]);
                if (c[i][j]=='.') l++;
            }
            scanf("\n");
        }
        if (l==n*m)
        {
            printf("0\n");
            continue;
        }
        bool p1=0;
        fo(i,1,n)
        {
            fo(j,1,m)
                if (c[i][j]=='X')
                {
                    fo(k,0,7)
                        if (c[i+fx[k][0]][j+fx[k][1]]=='X')
                        {
                            p1=1;
                            break;
                        }
                    if (p1) break;
                }
            if (p1) break;
        }
        if (p1)
        {
            printf("0\n");
            continue;
        }
        ans=0;
        dg(1,1,1);
        printf("%lld\n",(ans+mo*10)%mo);
    }
}

——2016.8.16

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值