状压DP(入门)

题目一链接
题目意思就是:给出n个物品,体积为w[i],现把其分成若干组,要求每组总体积<=W,问最小分组。(n<=18)

解题思路:用二进制枚举+dp

g[i]表示状态为i时当前背包剩余的体积
f[i]表示状态为i时所用最少的背包数
代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=20;
const int M=1<<18+5;
int n,m,a[N],f[M],g[M];
int main(){
    
    
    cin>>n>>m;
    int op=(1<<n);
    for(int i=0;i<n;i++)
        cin>>a[i];
    memset(f,0x3f,sizeof(f));
    f[0]=1; g[0]=m;
    for(int i=0;i<op;i++){
    
    //枚举所有的情况
        for(int j=0;j<n;j++){
    
    //选择第几个物品
            if((1<<j) & i) continue;//物体已经被选了 
            if(g[i]>=a[j]  && f[i|(1<<j)]>=f[i]){
    
     //物体可以放进  
            //i|(1<<j)指的是当前枚举的情况加上当前选的物品
               f[i|(1<<j)]=f[i];
               g[i|(1<<j)]=max(g[i|(1<<j)],g[i]-a[j]);
            }
            else if(g[i]<a[j] && f[i|(1<<j)]>=f[i]+1){
    
     //包放不下了 
               f[i|(1<<j)]=f[i]+1;
               g[i|(1<<j)]=max(g[i|(1<<j)],m-a[j]);            
            }
        }
    }    
    printf("%d",f[(1<<n)-1]); 
    return 0;
}

题二

给定一个n*m的矩阵(n,m<=20),1表示这个格子能选,0表示这个格子不能选,你需要选出尽可能多的格子,且保证选出来的所有格子直接不相邻(没有公共边)

例如

输入
n=2,m=3
1 1 1
0 1 0
输出

3

思路:用二进制枚举+状压dp
这一行的状态为now,下一行的状态为prev,只要
(now & prev)== 0 就行了
此外,我们还需要判断当前行是否合法,只要
(now | flag)== flag 即可

#include<bits/stdc++.h>
 
using namespace std;
 
const int MAX_N = 20;
const int MAX_M = 20;
int state[MAX_N + 1];
int dp[MAX_N + 1][1 << MAX_M];
 
bool not_intersect(int now, int prev) {
    
    
    return (now & prev) == 0;
}
 
bool fit(int now, int flag) {
    
    
    return (now | flag) == flag;
}
bool ok(int x) {
    
    
    // 行内自己是否相交
    return (x & (x / 2)) == 0;      //说明没有相邻的点
}
int count(int now) {
    
    
    int s = 0;  // 统计 now 的二进制形式中有多少个 1
    while (now) {
    
    
        s += (now & 1);  // 判断 now 二进制的最后一位是否为 1,如果是则累加
        now >>= 1;  // now 右移一位
    }
    return s;
}
 
int main() {
    
    
    int n, m;
    cin >> n >> m;
    // 初始化所有数组
    for (int i = 1; i <= n; ++i) {
    
    
        for (int j = 0; j < m; ++j) {
    
    
            int flag;
            cin >> flag;
            state[i] |= (1 << j) * flag;  // 将 (i,j) 格子的状态放入 state[i] 中,state[i] 表示第 i 行的可选格子组成的集合
        }
    }
    for (int i = 1; i <= n; ++i) {
    
    
        for (int j = 0; j < (1 << m); ++j) {
    
      // 枚举当前行的状态
            if (!ok(j) || !fit(j, state[i])) {
    
      // 如果当前行状态不合法则不执行后面的枚举
                continue;
            }
            int cnt = count(j);  // 统计当前行一共选了多少个格子
            for (int k = 0; k < (1 << m); ++k) {
    
    
                if (ok(k) && fit(k, state[i - 1]) && not_intersect(j, k)) {
    
      // 判断前一行是否合法和当前行和前一行的方案是否冲突
                    dp[i][j] = max(dp[i][j], dp[i - 1][k] + cnt);  // 更新当前行、当前状态的最优解
                }
            }
        }
    }
    int ans = 0;  // 保存最终答案
    for (int i = 0; i < (1 << m); ++i) {
    
    
        ans = max(ans, dp[n][i]);  // 枚举所有状态,更新最大值
    }
    cout << ans << endl;
    return 0;
}
 
 

猜你喜欢

转载自blog.csdn.net/weixin_46001550/article/details/107730317