棋盘式(基于连通性)DP模型总结

导语

  状压DP分两大类,一类是集合式,另一类就是棋盘式(即基于连通性)。

  其中,集合式状压DP难度相较后者略大,形式复杂多变。而棋盘式状压DP的题目样式都相差不多,解法也都殊途同归,因此将这一类题进行总结,不难归结出一套解题模板。

正文

  我们先看以下三个例题。

国王

题目链接

算法概述

  状态表示:f[i,j,s]表示已经在前i行放了j个国王,且第i行的状态为s的方案数。

  状态转移:考虑第i-1行所有合法状态x,则f[i,j,s]=∑f[i-1,j-count(s),x],其中count(s)表示s中国王的个数。

  算法流程:

  1.考虑将一行的状态进行压缩,然后预处理所有合法状态。

  2.枚举所有合法状态,对于每一种合法状态,处理该状态可由哪些状态转移过来。

  3.循环枚举DP状态,进行状态转移。

参考代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=12;

int stt[1<<N];
int cnt[1<<N];
int pre[1<<N][1<<N];
int sum[1<<N];
ll f[N][N*N][1<<N];
int n,m,cnt_stt;

bool check(int state)
{
    for(int i=0;i<n-1;i++)
        if(state>>i&1 && state>>i+1&1)return false;
    return true;
}

int count(int state)
{
    int ans=0;
    for(int i=0;i<n;i++)
        if(state>>i&1)ans++;
    return ans;
}

int main()
{
    scanf("%d%d",&n,&m);
    
    for(int i=0;i<(1<<n);i++)if(check(i))stt[++cnt_stt]=i,cnt[i]=count(i);
    
    for(int i=1;i<=cnt_stt;i++)
        for(int j=1;j<=cnt_stt;j++)
            if((stt[i]&stt[j])==0 && check(stt[i]|stt[j]))pre[stt[i]][++sum[stt[i]]]=stt[j];
    
    f[0][0][0]=1;
    for(int i=1;i<=n+1;i++)
        for(int j=0;j<=m;j++)
            for(int s=1;s<=cnt_stt;s++)
            {
                int a=stt[s];
                if(j<cnt[a])continue;
                for(int x=1;x<=sum[a];x++)
                {
                    int b=pre[a][x];
                    f[i][j][a]+=f[i-1][j-cnt[a]][b];
                }
            }
    
    printf("%lld\n",f[n+1][m][0]);
    
    return 0;
}

玉米田

题目链接

算法概述

  状态表示:f[i,a]表示已经种了前i行,且第i行的状态为a的方案数。

  状态转移:考虑第i-1行的所有合法状态b,则f[i,a]=∑f[i-1,b]。

  算法流程:

  1.预处理所有合法状态。

  2.预处理对于每一个合法状态,能够转移到其的所有合法状态的集合。

  3.循环状态表示,考虑当前行状态是否与地图限制相矛盾,然后循环状态转移。

参考代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=14,M=1<<12,mod=1e8;

int stt[1<<N];
int cnt[1<<N];
int pre[M][M];
int f[N][1<<N];
int g[N];
int n,m,cnt_stt;

bool check(int state)
{
    for(int i=0;i<m-1;i++)
        if(state>>i&1 && state>>i+1&1)return false;
    return true;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=0;j<m;j++)
        {
            int x;scanf("%d",&x);
            g[i]+=!x<<j;
        }
    
    for(int i=0;i<(1<<m);i++)if(check(i))stt[++cnt_stt]=i;
    
    for(int i=1;i<=cnt_stt;i++)
        for(int j=1;j<=cnt_stt;j++)
            if((stt[i]&stt[j])==0)pre[stt[i]][++cnt[stt[i]]]=stt[j];
            
    f[0][0]=1;
    for(int i=1;i<=n+1;i++)
        for(int j=1;j<=cnt_stt;j++)
        {
            int a=stt[j];
            if(g[i]&a)continue;
            for(int s=1;s<=cnt[a];s++)
            {
                int b=pre[a][s];
                (f[i][a]+=f[i-1][b])%=mod;
            }
        }
    
    printf("%d\n",f[n+1][0]);
    
    return 0;
}

炮兵阵地

题目链接

算法概述

  状态表示:

  状态转移:

  算法流程:

参考代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=110,M=11;

int stt[1<<M];
int cnt[1<<M];
int f[2][1<<M][1<<M];
int g[N];
int n,m,cnt_stt;

bool check(int state)
{
    for(int i=0;i<=m-2;i++)
        if((state>>i&1) && (state>>i+1&1 || state>>i+2&1))return false;
    return true;
}

int count(int state)
{
    int ans=0;
    for(int i=0;i<m;i++)ans+=state>>i&1;
    return ans;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        static char ch[M];
        scanf("%s",ch);
        for(int j=0;j<m;j++)
            if(ch[j]=='H')g[i]+=1<<j;
    }

    for(int i=0;i<(1<<m);i++)
        if(check(i))stt[++cnt_stt]=i,cnt[i]=count(i);

    for(int i=1;i<=n+2;i++)
        for(int j=1;j<=cnt_stt;j++)
            for(int k=1;k<=cnt_stt;k++)
            {
                int a=stt[j],b=stt[k];
                if(g[i-1]&a|g[i]&b)continue;
                for(int x=1;x<=cnt_stt;x++)
                {
                    int c=stt[x];
                    if((a&b)|(a&c)|(b&c))continue;
                    f[i&1][a][b]=max(f[i&1][a][b],f[i-1&1][c][a]+cnt[b]);
                }
            }
    
    printf("%d\n",f[n+2&1][0][0]);
    
    return 0;
}

模型总结

猜你喜欢

转载自www.cnblogs.com/ninedream/p/13404965.html