zoukankan      html  css  js  c++  java
  • 【BZOJ】P2669 局部极小值

    装压dp+容斥

    由于当n=4,m=7时,局部极小值(下面简称极小值)最多只有8个。(自己模拟一下)
    这样,我们就可以装压了。

    状态定义

    (dp_{i,j})表示当前放到第i,极小值的状态为j时的方案数

    状态转移

    分两种情况:
    1.在极小值中放i:
    (dp_{i,j<<(1<<(k-1))}+=dp_{i-1,j}(k otin j))
    2.在其他点钟放i
    (dp_{i,j}+=dp_{i,j-1} imes A)
    (A表示可以放的方案数)
    对于A,我们可以预处理出每个j状态可行的方案数

    此时的答案就是(dp_{n imes m,(1<<tot)-1}) (tot表示极小值的个数)

    思考与质疑

    想一想,要是在放的过程中某一个不是极小值的点变成极小值了咋办?
    可以枚举那些点变成了极小值,每次计算dp值后在更新最终答案。
    考虑到这些可能会有重合,(1个非极小值点变成了极小值必定包含了2个点变成了极小值的情况)
    这就需要容斥了。

    大致流程

    1.开个dfs,枚举那些非极小值点可以变成极小值
    2.当dfs到终点时,dp一下,并将最终答案更新
    总之,就是dfs套dp

    代码:

    
    #include<bits/stdc++.h>
    #define int long long
    #define MOD 12345678
    using namespace std;
    int n,m,X[101],Y[101],tot,ans,Size[(1<<8)+10];
    char mp[11][11];
    bool can[11][11],mark[11][11];
    const int mx[]= {1,0,-1,0,1,1,-1,-1,0},my[]= {0,1,0,-1,1,-1,1,-1,0};
    int dp[30][(1<<8)+10],sum[(1<<8)+10];
    int DP() {
        memset(dp,0,sizeof(dp));
        memset(sum,0,sizeof(sum));
        for(int i=0; i<(1<<tot); i++) {
            int res=0;
            memset(mark,false,sizeof(mark));
            for(int j=1; j<=tot; j++) {
                if((1<<(j-1))&i)continue;
                mark[X[j]][Y[j]]=true;
            }
            for(int j=1; j<=n; j++) {
                for(int k=1; k<=m; k++) {
                    bool flag=true;
                    for(int o=0; o<=8; o++) {
                        int nx=j+mx[o],ny=k+my[o];
                        if(mark[nx][ny]||can[j][k]) {
                            flag=false;
                            break;
                        }
                    }
                    res+=flag;
                }
            }
            sum[i]=res;
        }
        dp[0][0]=1;
        for(int i=1; i<=n*m; i++) {
            for(int j=0; j<(1<<tot); j++) {
                if(Size[j]>i)continue;
                dp[i][j]+=dp[i-1][j]*(max(sum[j]+Size[j]-i+1,0LL)),dp[i][j]%=MOD;
                for(int k=1; k<=tot; k++) {
                    if((1<<(k-1))&j)continue;
                    dp[i][j|(1<<(k-1))]+=dp[i-1][j],dp[i][j|(1<<(k-1))]%=MOD;
                }
            }
        }
        return dp[n*m][(1<<tot)-1];
    }
    bool check(int x,int y) {
        for(int i=0; i<=8; i++) {
            int nx=x+mx[i],ny=y+my[i];
            if(can[nx][ny])return false;
        }
        return true;
    }
    void DFS(int x,int y,int pos) {
        if(y==m+1)x++,y=1;
        if(x==n+1) {
            ans+=pos*DP(),ans+=MOD,ans%=MOD;
            return;
        }
        DFS(x,y+1,pos);
        if(check(x,y)) {
            can[x][y]=true,X[++tot]=x,Y[tot]=y;
            DFS(x,y+1,pos*-1);
            can[x][y]=false,tot--;
        }
    }
    signed main() {
        for(int i=1; i<(1<<8); i++)Size[i]=Size[i^(i&-i)]+1;
        scanf("%lld %lld",&n,&m);
        for(int i=1; i<=n; i++)scanf("%s",mp[i]+1);
        for(int i=1; i<=n; i++) {
            for(int j=1; j<=m; j++) {
                if(mp[i][j]=='X') {
                    can[i][j]=true;
                    X[++tot]=i,Y[tot]=j;
                }
            }
        }
        DFS(1,1,1);
        cout<<ans;
        return 0;
    }
    
    
  • 相关阅读:
    Codeforces Round #218 (Div. 2) 题解
    Codeforces Round #201 (Div. 2) 题解
    Codeforces Round #200 (Div. 2) 题解
    php 的文件操作类
    php 文件系统函数及目录函数
    jQuery File Upload的使用
    PHP 方法,类与对象的相关函数学习
    vue-cli 里axios的使用
    vue学习记录(一)---基本指令
    php 数组函数学习
  • 原文地址:https://www.cnblogs.com/SillyTieT/p/11191581.html
Copyright © 2011-2022 走看看