题目一链接
题目意思就是:给出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]){
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;
while (now) {
s += (now & 1);
now >>= 1;
}
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;
}
}
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;
}