状压dp小结

本文总结了状压动态规划(DP)的应用,通过四个具体的洛谷题目(P3943星空、P1879玉米田、P2915奶牛混合起来、P2704炮兵阵地)进行讲解,阐述了如何利用差分思想、预处理和状态转移解决这类问题。状压DP常用于处理列数或行数较小的情况,通过二进制枚举和状态冲突判断来求解方案数。
摘要由CSDN通过智能技术生成

今天模拟又考了一个SPFA+状压dp,巧了,我又不会,华丽丽爆零。(实际上我啥都不会)

状压dp即用二进制暴力枚举然后状态转移。用f[i][j]表示第i行在状态j的时候的方案数,其中j用一个二进制数来表示。转移的时候只要判断与当前行和上一行(或是上几行)是否冲突即可,如果不冲突,f[i][j]=∑f[i−1][q]其中q为不冲突的状态。∑1≤i≤cnt​f[n][i] 就是最后的答案,cnt为总状态数。状态转移前要先用dfs预处理一下方案的是否可行,是否冲突。
状压dp题的一个鲜明特征就是题目中的列数或者行数往往非常小,只有几或者十几,否则压缩不下。

1.luoguP3943星空

利用差分思想,设b[i]=a[i]^a[i-1],则b数组中最多有2k个1。对a的某一个区间进行取反时,b只有头尾被取反。将此问题转化为最短路,即有2k个起点,步长有m种,用spfa跑一遍最短路,使2k个1全部消去即可。为了避免重复计算,每次转移时可以先固定选择最小的位置,然后再枚举另一个位置。

#include<bits/stdc++.h>
#define maxn 40005
#define maxx 1000000009
using namespace std;
int n,k,m,x,y,d[maxn],have[maxn],w[23],val[maxn],f[500010],dis[23][23];
bool inq[maxn];
int read(){
   
    int s=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){
   s=s*10+ch-'0';ch=getchar();}
    return s;
}
queue<pair<int,int> > q;
int main(){
   
    n=read();k=read();m=read();
    for(int i=0;i<=n+1;i++) val[i]=1;
    for(int i=1;i<=k;i++) x=read(),val[x]=0;
    k=0;
    for(int i=1;i<=n+1;i++)
        if(val[i]^val[i-1]) w[++k]=i,have[i]=k;
    for(int i=1;i<=m;i++) d[i]=read(),d[i]++;
    int to;
    for(int i=1;i<=k;i++)
        for(int j=1;j<=k;j++)
            if(i!=j)  dis[i][j]=maxx;
    for(int i=1;i<=k;i++){
   
        for(int j=0;j<=n+1;j++) inq[j]=false;
        q.push(make_pair(w[i],0)); inq[w[i]]=true;
        while(!q.empty()){
   
            x=q.front().first;y=q.front().second;q.pop();
            for(int j=1;j<=m;j++){
   
                to=x+d[j]-1;
                if(to
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值