精华内容
下载资源
问答
  • 状压DP

    2017-12-10 23:02:15
    状压DP

    状压DP

    知识要点

    一般数据很小,暴力又过不了

    常用方法

    然而并没有

    补充一点:1:判断一个数字x二进制下第i位是不是等于1。

    方法:if(((1<<(i-1))&x)>0)

    2:将一个数字x二进制下第i位更改成1。

    方法:x = x| ( 1<<(i-1) )

    3:把一个数字二进制下最靠右的第一个1去掉。

    方法:x=x&(x-1)

    例题

    1. 旅行商问题

    dp[S][i] 表示经过点的集合为S,当前到达i号结点的最小花费。

    S中访问过的为1,未访问为0

    dp[S][i] = min{dp[S^(1<<i)][k] + w(k, i)}  (k:S&(1<<k) != 0

    2. CodeForces 453B

    dp[i][j]表示前i个数达到j状态的最小的结果,j状态表示已经被用过的质数。因为a小于等于30,所以如果某个数超过60,那么选择1一定比它更优,所以我们能够用到的数的质因子也一定不会超过60,也才17个,所以我们只需要每次枚举数,然后通过之前的状态进行转移即可。

    3. CDOJ 1296

    Dp1[i]表示 这些点连在一起的时候,最少需要多少边。(其实是判断性问题)

    dp2[i]表示 这些点连在一起的时候,可以有其他的点也并入,最少需要多少边。

    dp3[i]表示 这些集合对应的点连在一起的时候,最少需要多少边

        (通过k个城市对,可以分成一些联通的集合 )

    dp4[i]表示 这些集合可以相互不连通的时候,最少需要多少边

    dp1 à dp2 à dp3 à dp4

    枚举子集

    展开全文
  • 状压dp

    2017-10-09 09:23:24
    状压dp
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn=1e6;
    const int mod=1e9;
    int dp[15][(1<<11)+100];//空间可优化至dp[2][];dp[r][j]:至r行j状态的方案数
    int mp[15][15];
    int n,m;
    int a[maxn];
    int mp_jud(int r,int st)
    {
        for(int i=m-1;i>=0;i--,st=st>>1) if(!mp[r][i]&&(st&1)) return 0;
        return 1;
    }
    int st_jud(int st)
    {
        while(st)
        {
            if((st&1)&&((st>>1)&1)) return 0;
            st=st>>1;
        }
        return 1;
    }
    void init() //可用来优化时间
    {
        int num=0;
        for(int i=0;i<(1<<11);i++)
        {
            if(st_jud(i)) a[num++]=i;
        }
    }
    int two_jud(int st1,int st2)
    {
        while(st1&&st2)
        {
            if((st1&1)&&(st2&1)) return 0;
            st1=st1>>1;
            st2=st2>>1;
        }
        return 1;//忘记bug
    }
    int solve()
    {
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++) //与从0行读入数据不符bug
        {
           for(int j=0;j<(1<<m);j++)
           {
               if(st_jud(j)&&mp_jud(i,j))
               {
                  if(i==1) dp[i][j]++;
                  else
                  {
                      for(int k=0;k<(1<<m);k++)
                      {
                          if(st_jud(k)&&mp_jud(i-1,k)&&two_jud(k,j))
                          {
                              dp[i][j]=(dp[i][j]+dp[i-1][k]%mod)%mod;
                          }
                      }
                  }
               }
           }
        }
        int ans=0;
        for(int i=0;i<(1<<m);i++) ans=(ans+dp[n][i])%mod;//方法总数为之和bug
        return ans;
    }
    int main()
    {
        //freopen("C:/Users/hzy/Desktop/11.txt","r",stdin);
        //init();
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            for(int i=1;i<=n;i++) //从0行读入bug
             for(int j=0;j<m;j++)
               scanf("%d",&mp[i][j]);
            cout<<solve()<<endl;
        }
        return 0;
    }//炮兵阵地poj 1185的简化版,第r行的状态只影响r-1行,时间也不用优化.
    

    展开全文
  • [状压dp] 蒙德里安的梦想(模板题+状压dp) 强相关: [状压dp] 小国王(状压dp) 1. 状压dp+棋盘式(基于连通性) 327. 玉米田 [状压dp] 小国王(状压dp) 是井字形,本题是十字形。 思路: 状态定义: f[i][s]:所有 ...

    0. 前言

    相关:

    强相关:

    1. 状压dp+棋盘式(基于连通性)

    292. 炮兵阵地

    在这里插入图片描述在这里插入图片描述

    [状压dp] 玉米田(状压dp) 是高度为 1 的十字形,本题是 高度为 2 的十字形。 那么状态转移就和前 2 层有关。且本题是求最大值,而非方案数

    思路:

    • 状态定义:
      • f[i][j][k]:摆放完前 i 行,且第 i-1 行摆放状态是 j,第 i 行摆放状态是 k 的所有摆放方案的最大值
    • 状态计算:
      • 假设第 i 行状态是 b,第 i-1 行状态是 a,第 i-2 行状态是 c

      • ((a&b) | (a&c) | (b&c)) = 0 则说明纵向不会出现冲突

      • (g[i-1]&a | g[i]&b)=0 则说明炮兵在平地上。在此与的优先级高于或的优先级,不用加括号

      • 状态转移方程: f[i][j][k] = max(f[i][j][k], f[i-1][c][j]+cnt[c])

    • 时间复杂度: n2m2m2m=O(n23m)=100230=1011n*2^m*2^m*2^m=O(n2^{3m})=100*2^{30}=10^{11} 这个时间复杂度…但是里面合法状态很少…依旧可以过

    滚动数组优化,不然空间直接爆炸。

    重点还是在状态定义及状态计算上。这里状态定义记录了第 i 行状态为 k、第 i-1 行状态为 j。那么找到最后一个不一样的点就是第 i-2 行,所以就依据第 i-2 行进行状态划分即可。

    代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    
    using namespace std;
    
    const int N = 11, M = 1 << 10;
    
    int n, m;
    int g[105];
    vector<int> state;
    int cnt[M];
    int f[2][M][M];     // 使用滚动数组,后两维都已经100w了...
    
    bool check(int x) {
        for (int i = 0; i < m; ++i)         // 针对列做状态压缩,同列三个不能有2个1
            if ((x >> i & 1) && ((x >> i + 1 & 1) | (x >> i + 2 & 1)))
                return false;
        
        return true;
    }
    
    int count(int x) {
        int res = 0;
        for (int i = 0; i < m; ++i) res += x >> i & 1;
        return res;
    }
    
    int main() {
        cin >> n >> m;
        
        for (int i = 1; i <= n; ++i)
            for (int j = 0; j < m; ++j) {
                char c;                     // 拿int类型读入char,读入全是0
                cin >> c;
                if (c == 'H')
                    g[i] += 1 << j;         // 二进制表示一行
            }
        
        for (int i = 0; i < 1 << m; ++i) 
            if (check(i)) {
                state.push_back(i);
                cnt[i] = count(i);
            }
        
        for (int i = 1; i <= n + 2; ++i)
            for (int j = 0; j < state.size(); ++j)              // j为第i-1行状态
                for (int k = 0; k < state.size(); ++k)          // k为第i行状态
                    for (int u = 0; u < state.size(); ++u) {    // u为第i-2行状态
                        int a = state[j], b = state[k], c = state[u];
                        if ((a & b) | (b & c) | (a & c)) continue; 
                        if (g[i - 1] & a | g[i] & b) continue;
                        f[i & 1][j][k] = max(f[i & 1][j][k], f[i - 1 & 1][u][j] + cnt[b]);
                    }
                    
        cout << f[n + 2 & 1][0][0] << endl;
        
        
        // 遍历写法,修改第一个 for (int i = 1; i <= n; ++i)
        /*
        int res = 0;
        for (int i = 0; i < state.size(); ++i) 
            for (int j = 0; j < state.size(); ++j)
                res = max(res, f[n & 1][i][j]);
        cout << res << endl;
        */
        
        return 0;
    }
    
    展开全文
  • [状压dp] 蒙德里安的梦想(模板题+状压dp) 强相关: [状压dp] 小国王(状压dp) 1. 状压dp+棋盘式(基于连通性) 327. 玉米田 [状压dp] 小国王(状压dp) 是井字形,本题是十字形。 思路: 状态定义: f[i][s]:摆放...

    0. 前言

    相关:

    强相关:

    1. 状压dp+棋盘式(基于连通性)

    327. 玉米田

    在这里插入图片描述
    [状压dp] 小国王(状压dp) 是井字形,本题是十字形。

    思路:

    • 状态定义:
      • f[i][s]:摆放完前 i 行,且第 i 行摆放状态是 s 的方案数
    • 状态计算:
      • sisi1s_i、s_{i-1} 二进制表示中无相邻 1 的时候,则横向无公共边
      • si&si1=0s_i\&s_{i-1}=0 时,则说明上下无公共边
      • ss 二进制表示 1 的位置,土地状态恰好为 0,则该状态无效
      • 状态转移方程: f[i][s]+=f[i-1][si-1]
    • 时间复杂度: n2n2n=O(n22n)=12224n*2^n*2^n=O(n2^{2n})=12*2^{24} 看着妥妥超时,但是里面合法状态很少…依旧可以过

    在此,head 数组存储对应关系,其实不必要存 state 数组的值。也可以存 state 数组中两个合法状态各自的下标。因为这也是一一映射的关系,且 dp 数组在枚举最后一维状态的时候,也可以存下标。这样代码能简洁很多…

    代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    
    using namespace std;
    
    const int N = 14, M = 1 << 12, MOD = 1e8;
    
    int n, m;
    int g[N];               // 将地图每一行抽象为二进制表示
    vector<int> state;
    vector<int> head[M];
    int f[N][M];
    
    bool check(int x) {
        for (int i = 0; i + 1< m; ++i) 
            if ((x >> i & 1) && (x >> i + 1 & 1))
                return false;
        
        return true;
    }
    
    int main() {
        cin >> n >> m;
        
        for (int i = 1; i <= n; ++i) 
            for (int j = 0; j < m; ++j) {
                int t;
                cin >> t;
                g[i] += !t << j;            // 很巧妙,是1的地方不能选,方便做&运算
            }
        
        for (int i = 0; i < 1 << m; ++i)
            if (check(i))
                state.push_back(i);
        
        for (int i = 0; i < state.size(); ++i)
            for (int j = 0; j < state.size(); ++j) {
                int a = state[i], b = state[j];
                if ((a & b) == 0) 
                    head[a].push_back(b);
            }
        
        f[0][0] = 1;
        for (int i = 1; i <= n + 1; ++i)
            for (int a = 0; a < state.size(); ++a)
                for (int b = 0; b < head[state[a]].size(); ++b) 
                    if ((g[i] & state[a]) == 0)
                        f[i][state[a]] = (f[i][state[a]] + f[i - 1][head[state[a]][b]]) % MOD;
        
        cout << f[n + 1][0] << endl;
        return 0;
    }
    

    同理 [状压dp] 小国王(状压dp),也可以用下标映射状态的思想,在 dp 数组中存下标即可。代码能简洁方便点。

    存下标,简洁代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    
    using namespace std;
    
    const int N = 14, M = 1 << 12, MOD = 1e8;
    
    int n, m;
    int g[N];               // 将地图每一行抽象为二进制表示
    vector<int> state;
    vector<int> head[M];
    int f[N][M];
    
    bool check(int x) {
        for (int i = 0; i + 1< m; ++i) 
            if ((x >> i & 1) && (x >> i + 1 & 1))
                return false;
        
        return true;
    }
    
    int main() {
        cin >> n >> m;
        
        for (int i = 1; i <= n; ++i) 
            for (int j = 0; j < m; ++j) {
                int t;
                cin >> t;
                g[i] += !t << j;            // 很巧妙,是1的地方不能选,方便做&运算
            }
        
        for (int i = 0; i < 1 << m; ++i)
            if (check(i))
                state.push_back(i);
        
        for (int i = 0; i < state.size(); ++i)
            for (int j = 0; j < state.size(); ++j) {
                int a = state[i], b = state[j];
                if ((a & b) == 0) 
                    head[i].push_back(j);               // 下标映射
            }
        
        f[0][0] = 1;
        for (int i = 1; i <= n + 1; ++i)
            for (int a = 0; a < state.size(); ++a)
                for (int b : head[a])
                    if ((g[i] & state[a]) == 0)
                        f[i][a] = (f[i][a] + f[i - 1][b]) % MOD;
        
        cout << f[n + 1][0] << endl;
        return 0;
    }
    
    展开全文
  • 状压dp2

    2019-09-24 11:02:39
    上一篇状压dp例题由于每个位置都含有一个非负数,所以不需要判断能不能每个位置是否能取, 而这一道题由于地图已经说明每个位置是否能取,所以需要多加一步:将地图二进制化 然后把第一行的状态初始化存储在dp中...
  • [状压dp] 蒙德里安的梦想(模板题+状压dp) 强相关: [状压dp] 小国王(状压dp) [状压dp] 玉米田(状压dp) 1. 状压dp+棋盘式(基于连通性) 524. 愤怒的小鸟 NOIP2016提高组 思路: 在此,该抛物线过原点,且开口向下...
  • [状压dp] 蒙德里安的梦想(模板题+状压dp) 1. 状压dp+棋盘式(基于连通性) 1064. 小国王 思路: 状压 dp,个人认为状态定义比较麻烦。剩下的就是简单的位运算。 尤其是最后一步的优化,是真的秀… 代码: #include ...
  • 状压dp 模板题 0. 前言 状压 dp 就是采用二进制数保存状态,方便进行位运算操作。例如 八皇后、八数码问题也都是采用了状态压缩的思想来使用一个二进制数唯一对应集合中的一个状态。 关键是要体会采用二进制数来...
  • 状压dp 模板题 0. 前言 状压 dp 就是采用二进制数保存状态,方便进行位运算操作。例如 八皇后、八数码问题也都是采用了状态压缩的思想来使用一个二进制数唯一对应集合中的一个状态。 关键是要体会采用二进制数来...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 20,838
精华内容 8,335
关键字:

状压dp