第一次接触状压DP

状压DP入门及理解

*(另类的暴力)*

     一般状态数不多的时候就会开数组,但是有的状态并不好表示,于是,状压DP就产生了。

    状压DP应该是分两类的,一类是压缩状态,另一类是舍弃状态。    我感觉初学状压DP难就难在二进制运算的应用,了解二进制运算符就显得十分重要。

    所以我们先看下表,如果有不会二进制简单应用的请点击https://blog.csdn.net/sinat_35121480/article/details/53510793(神犇请忽略...)

下面就可以看题了:

对于状压压缩,入门题[USACO06NOV]玉米田Corn Fields[SCOI2005]互不侵犯,思路基本上相同。

[USACO06NOV]玉米田Corn Fields

https://www.lydsy.com/JudgeOnline/problem.php?id=1725

我自己做了这两个题有两点体会,如下:

第一点(第一题):我们可以通过循环预处理出所有的状态,再在动态规划的过程中判断状态是否可行,方案数累加即可。

这样来,动态规划的方程就很好推出来了(初学者可能有点困难)。

F[ I ][ j ] 表示前I行,第I行状态为J的方案数,J存的是状态,用二进制表示。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<bitset>
using namespace std;//前 i 行  //当前行 状态为j的方案数
int n,m,a[15][15],g[1<<12],f[15][1<<12],mod=1e8;
int check(int x)
{
    if(!((x<<1)&x)&&!((x>>1)&x))
    return 1;
    else
    return 0;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)//转化成二进制
    g[i]=(g[i]<<1)+a[i][j];
    for(int i=0;i<(1<<m);i++)
    if(check(i)&&((i&g[1])==i))//后者表示在可以种植的地方选择(全选/部分/不选)地方去种,有点子集的感觉。
    f[1][i]=1;
        for(int i=2;i<=n;i++)//第i行
        {
            for(int j=0;j<(1<<m);j++)//第i行状态为J
            {
             if(check(j)&&((j&g[i])==j))
             for(int k=0;k<(1<<m);k++)//第i-1行状态为k
             {
             if(check(k)&&((k&g[i-1])==k)&&(!(k&j)))
             f[i][j]+=f[i-1][k],f[i][j]%=mod;
             }
            }
        }
    int ans=0;
    for(int i=0;i<(1<<m);i++)
    ans+=f[n][i],ans%=mod;
    printf("%d",ans%mod);
    return 0;
}

[SCOI2005]互不侵犯

https://www.lydsy.com/JudgeOnline/problem.php?id=1087

第二点(第二题):我们可以通过DFS搜索出所有符合情况的方案记录下来再进行动态规划。

DFS过程中如果搜到行的尽头,我们就保存状态再返回,否则就进行下一步搜索。

搜索分两种状态:1.当前格子不放国王,搜索下一个格子  2.当前格子放国王,就得跳过下一个格子搜索。(代码中有特别注释)

我们先不要管在列方向上的约束,只管每一行的国王不能放在一起,每一列的国王不能放在一起那是下面要考虑的问题。

当所有满足条件的行的状态都搜索出来之后,就可以动态规了,中间剔除列上不符要求的状态。

动态规划方程也跟上题的差不多:

F[ I ][ J ] [ K ] 表示前 I 行 ,第 i 行状态为J  总共选了K个国王的方案数。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long int
using namespace std;
ll n,t,top,sum[2000],zt[2000],f[20][1000][300],ans;//位置  状态   选了几个
void dfs(ll z,ll s,ll ci)
{
    if(ci>=n)
    {
        top++;
        zt[top]=z;
        sum[top]=s;
        return;
    }
    dfs(z,s,ci+1);//不选
    dfs(z+(1<<ci),s+1,ci+2);//选
}
int main(){
    scanf("%lld%lld",&n,&t);
    dfs(0,0,0);
    for(ll i=1;i<=top;i++)f[1][i][sum[i]]=1;
    for(ll i=2;i<=n;i++)
      for(ll j=1;j<=top;j++)
        for(ll k=1;k<=top;k++)
        {
            if(zt[j]&zt[k])continue;
            if((zt[j]<<1)&zt[k])continue;
            if((zt[k]<<1)&zt[j])continue;
            for(ll q=sum[j];q<=t;q++){
                f[i][j][q]+=f[i-1][k][q-sum[j]];
                //前i行 第i行状态为j  有q个国王的方案数。
            }
        }
    for(ll i=1;i<=top;i++)
    ans+=f[n][i][t];
    printf("%lld",ans);
    return 0;
}

嗯,这道题是右上角的大佬教我的。

我对于状压DP的理解刚刚入门,可能还有很多说的不妥当的地方希望各位神犇评论告知。

2018-07-28

19:17:14

原文地址:https://www.cnblogs.com/sky-zxz/p/9383116.html

时间: 2024-08-28 01:30:34

第一次接触状压DP的相关文章

HDU 4336 容斥原理 || 状压DP

状压DP :F(S)=Sum*F(S)+p(x1)*F(S^(1<<x1))+p(x2)*F(S^(1<<x2))...+1; F(S)表示取状态为S的牌的期望次数,Sum表示什么都不取得概率,p(x1)表示的是取x1的概率,最后要加一因为有又多拿了一次.整理一下就可以了. 1 #include <cstdio> 2 const int Maxn=23; 3 double F[1<<Maxn],p[Maxn]; 4 int n; 5 int main() 6

【NOI2015】寿司晚会 题解(状压DP)

[问题描述] 为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴.小 G 和小 W 作为参加 NOI 的选手,也被邀请参加了寿司晚宴. 在晚宴上,主办方为大家提供了 n?1 种不同的寿司,编号 1,2,3,?, n?1, 其中第 i 种寿司的美味度为 i+1  (即寿司的美味度为从 2 到 n ). 现在小 G 和小 W 希望每人选一些寿司种类来品尝,他们规定一种品尝方案 为不和谐的当且仅当:小 G 品尝的寿司种类中存在一种美味度为 x 的寿司,小 W 品尝的寿司中存在一种美味度为 y

BZOJ1087:[SCOI2005]互不侵犯King(状压DP)

[SCOI2005]互不侵犯King Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. Input 只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N) Output 方案数. Sample Input 3 2 Sample Output 16 分析: 经典的状压DP题目,可我竟然调了很长时间都没对,后来发现是DP枚举范围错

POJ 2411 状压DP经典

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 16771   Accepted: 9683 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

POJ 2686(状压DP

第一次做状压感觉那一长串for显示了这是个多么暴力的算法呢...1A了倒是挺顺的 #include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<utility> #include<vector> #include<cstring> #include<cmath> #define INF 0x3fffffff #d

BZOJ 1087 题解【状压DP】

1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3112  Solved: 1816[Submit][Status][Discuss] Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. Input 只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K &

【BZOJ-1097】旅游景点atr SPFA + 状压DP

1097: [POI2007]旅游景点atr Time Limit: 30 Sec  Memory Limit: 357 MBSubmit: 1531  Solved: 352[Submit][Status][Discuss] Description FGD想从成都去上海旅游.在旅途中他希望经过一些城市并在那里欣赏风景,品尝风味小吃或者做其他的有趣的事情.经过这些城市的顺序不是完全随意的,比如说FGD不希望在刚吃过一顿大餐之后立刻去下一个城市登山,而是希望去另外什么地方喝下午茶.幸运的是,FGD

HDU 1074 Doing Homework DP 状压DP

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1074 题目描述: 给你所有课程的截止时间和工作时长, 一次只能做一种作业, 问最少罚时几天 N <= 15 解题思路: 由于N很小, 所以第一反应就是状压DP, 我们可以用一个15位二进制数来表示各个课程做完还是没做完, 然后从 S 从 1 到 1 << N 枚举 i 从 1 到 N 枚举, 如果S & (1<<i) 有效则说明i 属于情况 S, 这样我们从上一步S -

【弱校胡策】2016.4.14 (bzoj2164)最短路+状压DP+矩阵乘法+高斯消元+树链剖分+线段树+背包DP

cyyz&qhyz&lwyz&gryz弱校胡策 命题人:cyyz ws_fqk T3暴力写挫了 50+10+0滚粗辣! 奇妙的约会(appointment.cpp/c/pas) [问题描述] DQS和sxb在网上结识后成为了非常好的朋友,并且都有着惊人 的OI水平.在NOI2333的比赛中,两人均拿到了金牌,并保送进入 HU/PKU.于是两人决定在这喜大普奔的时刻进行面基. NOI2333参赛选手众多,所以安排了n个考点,DQS在1号考点, 而sxb在n号考点.由于是举办全国性赛事