备战蓝桥杯---状态压缩DP基础1之棋盘问题

它只是一种手段,一种直观而高效地表示复杂状态的手段。

我们先来看一道比较基础的:

直接DFS是肯定不行,我们发现对某一行,只要它前面放的位置都一样,那么后面的结果也一样。

因此我们考虑用DP,并且只有0/1,我们用二进制压缩。

我们令fist表示前i行状态为st的个数。

我们易得状态转移方程为:fist=(第i行放在第j列)

同时我们保证(st'&(1<<(j-1))==0&&st'+1<<(j-1)==st。

但是我们会遇到一个问题:怎么枚举st?

其实,我们可以不用二维数组,从1二进制枚举到1<<(n)-1,因为得到它的肯定比他小,肯定是计算过的,因此我们可以这么做,这里假设状态为1101,那我们如何获得1100,1001,0101呢?

用lowerbit操作,x&-x就得到了X中最低位的1及其后面的0,这样子就ok了。

下面是AC代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int n,m,dp[1<<20],bu[25],x,y;
int lowerbit(int x){
	return (x&(-x));
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		bu[x]+=1<<(y-1);
	}
	int j,cnt;
	dp[0]=1;
	for(int i=1;i<=((1<<n)-1);i++){
		j=i;
		cnt=0;
		while(j!=0){
			j-=lowerbit(j);
			cnt++;
		}
		j=i;
		while(j!=0){
			int fk=lowerbit(j);
			if((fk&bu[cnt])==0){
				dp[i]+=dp[i^fk];
			}
			j-=fk;
		}
	}
	cout<<dp[(1<<n)-1];	
}

让我们看看比较有意思的题吧:

首先,如果我们一个一个看DFS的话,我们会发现第二个位置不像八皇后范围很容易确认+81个格子,时间不允许,用一般的DP实现起来很麻烦,因为当我们要放一个国王时,我们还得知道能不能放,即不满足无后效性。

于是我们可以换一种思路:

我们一行一行看,这样子,当我们放当前行时,关注的只有上一行,而在这一行只要不相邻即可。

我们令放了国王为1,没放为0.

现在我们看是否合法:

1.同一行不相邻:(x&(x<<1))==0(注意加括号)

2.如果上一行的状态为x,当前为y,xy要满足什么条件合法?

(x&y)==0 (x&(y<<1))==0 (x&(y>>1))==0

因此,我们令fijk表示第i行,状态为k时已经放了j个的方案数;

易得状态转移方程:

fijk+=fi-1j-num(k)p(k自己合法,p也合法,(k&p)==0 (k&(p<<1))==0 (k&(p>>1))==0)

下面是AC代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int n,k,num[1500];
long long dp[10][90][1000];
int calc(int num){
    int ans=0;
    while(num!=0){
        if((num&1)==1) ans++;
        num>>=1;
    }
    return ans;
}
int main(){
    cin>>n>>k;
    dp[0][0][0]=1;
    for(int i=0;i<(1<<n);i++){//打表
        num[i]=calc(i);
    }
    for(int i=1;i<=n;i++){
        for(int j=0;j<=k;j++){
            for(int st=0;st<(1<<n);st++){
                if((st&(st<<1))!=0) continue;
                if(num[st]>j) continue;
                for(int p=0;p<(1<<n);p++){
                    if((p&(p<<1))!=0) continue;
                    if((p&(st<<1))!=0) continue;
                    if((p&(st>>1))!=0) continue;
                    if((p&(st))!=0) continue;
                    dp[i][j][st]+=dp[i-1][j-num[st]][p];
                }
            }
        }
    }
    long long ans=0;
    for(int st=0;st<(1<<n);st++){
        ans+=dp[n][k][st];
    }
    cout<<ans;
}
相关推荐
同勉共进23 分钟前
记一例 vibe coding + gcc bug 导致的线程池死锁问题
c++·线程池·gcc·死锁·vibe coding
我叫洋洋26 分钟前
C ++ [ hello world ]
c语言·c++·算法
奋发向前wcx2 小时前
y1,y2总复习笔记5 2026.7.19
数据结构·笔记·算法
王维同学4 小时前
[自学][Windows C++]RunOnceEx 注册表分组键的安全遍历
c++·windows·安全·开源
战族狼魂5 小时前
高频面试题精选:分治与AI Agent架构
人工智能·算法·大模型·大语言模型
李剑一5 小时前
你用过网易的将军令嘛?它底层实现账号保护的原理相当简单粗暴
算法
Scott9999HH5 小时前
告别流量波动玄学!从法拉第电磁定律到底层 C/C++ 流量累积算法,深度解密工业级电磁流量计选型与开发
c语言·c++·算法
中达瑞和-高光谱·多光谱6 小时前
1nm到8nm光谱分辨率,你的应用该选哪款光谱相机?
算法·高光谱·高光谱相机
脱胎换骨-军哥6 小时前
C++/Rust无缝互操作:混合系统新常态
开发语言·c++·rust
香辣牛肉饭6 小时前
【算法】动态规划 最长公共子序列(LCS)
经验分享·笔记·算法·动态规划