• 状压dp入门(模板题+思维题)


    洛谷P1879 [USACO06NOV]玉米田Corn Fields

    入门题

    /*
    洛谷P1879 
    n*m矩阵 有些位置可选 有些不可选 不能选择两块相邻的土地
    状压裸题:记录状态->判断矛盾->处理答案 
    */
    #include<bits/stdc++.h>
    using namespace std;
    #define N 13
    #define mod 100000000
    int a[N][N],dp[N][1<<N],sta[1<<N];
    bool get(int i)//判断是否有连续的 1 
    {
        if(i&(i<<1)) return false;
        return true;
    }
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
              scanf("%d",&a[i][j]);
              if(a[i][j]) a[i][j]=0;
              else a[i][j]=1;
              sta[i]=(sta[i]<<1)+a[i][j];//sta为每一行的状态 
        }
        for(int i=0;i<(1<<m);i++) if(!(i&sta[1])&&get(i)) dp[1][i]++;//预处理第一行 
        for(int i=2;i<=n;i++)
         for(int j=0;j<(1<<m);j++)
          if(!(j&sta[i])&&get(j)){
              
              for(int k=0;k<(1<<m);k++){
                  if((k&sta[i-1])||(j&k)||!get(k)) continue;//j&k判断是否两行有矛盾状态 
                    dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
              }
               
          }
        int ans=0;
        for(int i=0;i<(1<<m);i++) ans=(dp[n][i]+ans)%mod;
        printf("%d
    ",ans);
    }
    /*
    2 3
    1 1 1
    0 1 0
    */

    洛谷 P1896 [SCOI2005]互不侵犯

    /*
    洛谷P1896
    题意:在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。
    国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子
    思路:判断是否有相邻的1 判断是否和上一行隔一个有一个1 
    */
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define N 10
    ll dp[N][1<<N][N*N];//多定义一维x 表示到这一行已经放了多少个国王
    int get(int x)
    {
        int ans=0;
        while(x){ if(x&1) ans++; x>>=1; }//求这一行的 1 的个数 
        return ans;
    }
    bool check(int x)
    {
        if(x&(x<<1)) return false;//求有没有相邻的 1 
        return true;
    }
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=0;i<(1<<n);i++) if(check(i)) dp[1][i][get(i)]++; 
        for(int i=2;i<=n;i++)
          for(int j=0;j<(1<<n);j++)
           if(check(j)){
               for(int k=0;k<(1<<n);k++){
                   if(!check(k)||(j&k)||j&(k<<1)||(j<<1)&k) continue;
                   for(int x=0;x<=m;x++)
                   dp[i][j][x+get(j)]+=dp[i-1][k][x];
               }
        }
        ll ans=0;
        for(int i=0;i<(1<<n);i++) ans+=dp[n][i][m];
        printf("%lld
    ",ans);
    }

    洛谷P2704[NOI2001]炮兵阵地

    /*
    炮兵阵地
    普通状压 
    */
    #include<bits/stdc++.h>
    using namespace std;
    #define N 105
    #define M 12
    int a[N][N],sta[1<<M],dp[2][1<<M][1<<M],get[1<<M];
    bool check1(int x)
    {
        if( ( (x<<1)&x ) || ( (x<<2)&x ) ) return false;
        return true;
    }
    char op[15];
    int main()
    {
        int n,m,ans=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%s",op);
            for(int j=0;j<m;j++){
                if(op[j]=='H') a[i][j]=1;
                sta[i]=(sta[i]<<1)+a[i][j];//()!!!!!位运算的级别真的很低的 每当遇到不知道是什么问题的时候 考虑给位运算加个括号 
            }
        }
        for(int i=0;i<(1<<m);i++){//预处理每一个状态对应炮兵的个数 
            int now=i,cnt=0;
            while(now){
                if(now&1) cnt++;
                now>>=1;
            }
            get[i]=cnt;
        }
        for(int i=0;i<(1<<m);i++) if( check1(i) && !(i&sta[1]) ) dp[1][i][0]=get[i];//预处理第一行 
        for(int i=0;i<(1<<m);i++)//预处理前两行 
         for(int j=0;j<(1<<m);j++) 
          if( !(i&sta[1]) && !(j&sta[2]) && check1(i) && check1(j) && !(i&j) ) 
           dp[0][j][i]=max(dp[0][j][i],dp[1][i][0]+get[j]);
        //看起来是10^11 但实际上远远达不到!! 
        for(int i=3;i<=n;i++)
         for(int j=0;j<(1<<m);j++)
          if( check1(j) && !(j&sta[i]) ){//自己一行的状态不矛盾+与别人状态不矛盾+没有在山峰上放了炮兵 
              for(int k=0;k<(1<<m);k++)//枚举上一行 
              if( !(k&sta[i-1]) && check1(k) && !(k&j) ){
                  for(int p=0;p<(1<<m);p++)//枚举上上行 
                  if( !(j&p) && !(p&sta[i-2]) && check1(p) && !(p&k) )
                  dp[i&1][j][k]=max(dp[i&1][j][k],dp[(i&1)^1][k][p]+get[j]);
            }
        }
        for(int i=0;i<(1<<m);i++)
         for(int j=0;j<(1<<m);j++)
          ans=max(ans,dp[n&1][i][j]);
        printf("%d
    ",ans);
    }
    /*
    6 5
    PPPPP
    PPPPP
    PPPPP
    PPPPP
    PPPPP
    PPPPP
    */

    洛谷P2051 [AHOI2009]中国象棋

    /*
    洛谷P2051 
    题意:n*m的矩阵 每列最多放两个格子 每行最多放一个格子 求方案数
    思路:状压,但n m太大 不能将所有状态压入  又
    发现不必知道每一个详细状态 只需要知道每一行每一列放了多少个棋子
    于是考虑维护列所放的状态(只维护放的个数) 枚举行进行dp转移
    即固定行只选两个 对列进行转移 
    */
    #include<bits/stdc++.h>
    using namespace std;
    #define mod 9999973
    #define ll long long
    #define N 105
    //加强版状压 难点在于dp的定义
    //定义 dp[i][j][k]为第i行 放了j列只有一个的格子 放了k列有两个的格子 
    ll dp[N][N][N]; 
    ll C(int n)
    {
        return (n-1)*n/2;
    }
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        dp[0][0][0]=1;
        for(int i=0;i<=n-1;i++)//1不好先初始化 就由i转移到i+1 
         for(int j=0;j<=m;j++)
          for(int k=0;j+k<=m;k++)//注意范围!! j+k<=m 
          if(dp[i][j][k]){//排除无用状态 
              //每一行只能放两个棋子 分类讨论 
              dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%mod;//什么都不放 
              if(m-j-k>=1) dp[i+1][j+1][k]=(dp[i+1][j+1][k]+dp[i][j][k]*(m-j-k))%mod;//放一个在没有棋子的一列 
              if(j>=1)     dp[i+1][j-1][k+1]=(dp[i+1][j-1][k+1]+dp[i][j][k]*j%mod)%mod;//放一个在有一个棋子的一列 
              if(m-j-k>=2) dp[i+1][j+2][k]=(dp[i+1][j+2][k]+dp[i][j][k]*C(m-j-k)%mod)%mod;//放两个在没有棋子的两列 
              if(j>=2)     dp[i+1][j-2][k+2]=(dp[i+1][j-2][k+2]+dp[i][j][k]*C(j)%mod)%mod;//放两个在有棋子的两列
            if(m-j-k>=1&&j>=1) dp[i+1][j][k+1]=(dp[i+1][j][k+1]+dp[i][j][k]*(m-j-k)*j);
            //一个放没有棋子的 一个放有一个棋子的 
          }
        ll ans=0;
        for(int i=0;i<=m;i++)
         for(int j=0;i+j<=m;j++) 
          ans=(ans+dp[n][i][j])%mod;
        printf("%lld
    ",ans);
    }

    待更新。。。

  • 相关阅读:
    cento7快速修改主机名和修改root密码
    [goolegke]nginxingress建立测试
    filebeat安装读取nginx json日志
    MySQL索引背后的数据结构及算法原理
    Lua脚本在redis分布式锁场景的运用
    Sentinel实现限流
    java架构技术流程图
    mybatis数据加解密处理方案
    vue 自定义代码片段
    node项目vue 自动化部署之pm2
  • 原文地址:https://www.cnblogs.com/mowanying/p/11228414.html
Copyright © 2020-2023  润新知