专栏文章
状压DP
个人记录参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minid578
- 此快照首次捕获于
- 2025/12/02 02:54 3 个月前
- 此快照最后确认于
- 2025/12/02 02:54 3 个月前
状态压缩,一种优化。
快速判断是否为状压带派
首先看到数据范围,如果有变量较小,且为什么集合问题,放置问题,排列问题,差不多都为状压带派。
状压套路
常见的状压一般存本行会波及的行的状态,第几行等等。可以用一个动态数组来记录合法的一行的状态,然后基本只用循环,再判断多行之间是否合法一般的状态压缩,前几行都是要自己初始化的。要注意是否需要从开始。
状态压缩常用命令
CPP__builtin_popcount(i) 找出i里面有几个数位为1,适用与int
__builtin_popcountll(i) 找出i里面有几个数位为1,适用与long long
i|=(1<<j) 设置第j位为1
i&=~(1<<j) 设置第j位为0
// 检查第j位是否为1
if (i & (1 << j)) {
// 第j位为1
}
// 检查第j位是否为0
if (!(i & (1 << j))) {
// 第j位为0
}
例题1
转移方程为
CPP#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int dp[11][105][(1<<10)+5];
int num[(1<<10)+5];
vector<int>ans;
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=0;i<(1<<n);i++){
if((i&(i>>1))==0&&(i&(i<<1))==0){
ans.push_back(i);
num[i]=__builtin_popcount(i);
}
}
for(auto i:ans)
if(num[i]<=m)
dp[1][num[i]][i]=1;
for(int i=2;i<=n;i++){
for(int l=0;l<=m;l++)
for(auto j:ans)//当前这一行
for(auto k:ans){//上一行
if((k&j)==0&&((j>>1)&k)==0&&((k>>1)&j)==0&&num[j]<=l)
dp[i][l][j]+=dp[i-1][l-num[j]][k];
}
}
int sum=0;
for(auto i:ans){
sum+=dp[n][m][i];
}
cout<<sum;
return 0;
}
例题2
这道题目也较为好想,但难以调教。
首先,把行内合格的状态处理出来加到数组里面。
很明显,我们要枚举行装态,与第几行开始。
状态定义
状态转移方程
约束条件
- 地形约束:
- 相邻行约束:
- 隔行约束:
- 行内约束:
变量说明
- :当前行号
- :当前行状态
- :上一行状态
- :上两行状态
- :状态 的炮兵数量
- :第 行地形掩码
- :所有合法状态的集合
初始化
当 时:
当 时:
最终答案
代码
CPP#include<bits/stdc++.h>
using namespace std;
const int N=1000;
int dp[1005][1<<N][1<<N];
int n,m;
int a[105];
int num[1<<N];
int ans=0;
vector<int>anss;
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
string s;
cin>>s;
for(int j=0;j<m;j++){
if(s[j]=='H'){
a[i]|=(1<<j);
}
}
}
for(int i=0;i<(1<<m);i++){
if(((i&(i<<1))==0)&&((i&(i<<2))==0)){
anss.push_back(i);
num[i]=__builtin_popcount(i);
}
}
if(n==1){
for(int i:anss){
if((i&a[1])==0){
dp[1][i][0]=num[i];
ans=max(ans,dp[1][i][0]);
}
}
cout<<ans;
return 0;
}
//cout<<anss.size();
for(int i:anss){
for(int j:anss){
if(((j&i)!=0)||((i&a[2])!=0)||((j&a[1])!=0))continue;
dp[2][i][j]=num[i]+num[j];
ans=max(ans,dp[2][i][j]);
}
}
for(int i=3;i<=n;i++){
for(int j:anss){//这一行
if(((j&a[i])!=0))continue;
for(int k:anss){//上一行
if(((j&k)!=0)||((k&a[i-1])!=0))continue;
for(int o:anss){//上两行
if(((o&k)!=0)||((o&j)!=0)||((o&a[i-2])!=0))continue;
dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][o]+num[j]);
ans=max(ans,dp[i][j][k]);
}
}
}
}
// for(int k=2;k<=n;k++)
// for(int i:anss){
// for(int j:anss){
// cout<<dp[k][i][j]<<" ";
// }
// cout<<endl;
// }
cout<<ans;
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...