01揹包問題:
題意:n個物品一個m容量的揹包,n個物品有need[i]的體積消耗,以及權值value[i] ,問m容量裝n個物品能得到的最大權值是多少。
做法:01揹包介紹:博客
代碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read()
{
ll x=0,w=1; char c=getchar();
while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
return w==1?x:-x;
}
const int N=5e2+10,M=1e5+10;
int n,m,dp[M],need[N],value[N];
int main()
{
n=read(),m=read();
for(int i=1;i<=n;++i){
need[i]=read(),value[i]=read();
}
for(int i=1;i<=n;++i){
for(int j=m;j>=need[i];--j){
dp[j]=max(dp[j],dp[j-need[i]]+value[i]);
}
}
printf("%d\n",dp[m]);
}
Bitset優化01揹包
需要更正一個地方是,這裏的bitset優化的不是樸素的01揹包,而是隻有01狀態的多重揹包。
之前的博客:[博客C回到過去] [題目鏈接 C回到過去]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100010;
bitset<N>f[451],g[451];
int a[N],cnt[N],ans[N],anss;
int main() {
int n,m;
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i) scanf("%d",&a[i]);
sort(a + 1,a + n + 1);
int tot = 0;
for(int i = 1;i <= n;++i) {
if(a[i] != a[i - 1]) a[++tot] = a[i];
cnt[tot]++;
}
n = tot;
f[0][0] = 1;
g[n + 1][0] = 1;
for(int i = 1;i <= n;++i) {
int k = a[i],tmp = cnt[i];//f[i]|=f[i]<<a[i]代表f[i]取一個a[i]時的狀態轉移
f[i] = f[i - 1];
for(int j = 1;j <= tmp;tmp -= j,j <<= 1,k <<= 1) f[i] |= f[i] << k;
// 運用倍增(二進制)的思想,節約時間 並且能夠覆蓋所有的狀態
//比如現在有5個1
//j=1 1一個1 可以 得到 bitset狀態:000011(從後往前數從低位到高位,低位從0開始)
//j=2 那麼倍增一下,兩個1 :之前的狀態00011移兩位得 001100
//或上之前得000011 得 001111 是不是得到0,1,2,3,都是1的情況
//需要注意的是現在我們應該是消耗了三個1了 j目前還是2。那麼tmp就不是一成不變的,所以tmp-=j
//接着j繼續乘2 j =4 由於5個1消耗了3 剩餘兩個,小於j 跳出for循環
//因爲有剩餘的部分,就繼續組合一下
f[i] |= f[i] << (a[i] * tmp);
}
for(int i = n;i >= 1;--i) {
int k = a[i],tmp = cnt[i];
g[i] = g[i + 1];
for(int j = 1;j <= tmp;tmp -= j,j <<= 1,k <<= 1) g[i] |= g[i] << k;
g[i] |= g[i] << (a[i] * tmp);
}
for(int i = 1;i <= n;++i) {
int flag = 0;
for(int j = 0;j <= m;++j) {
if(f[i - 1][j] & g[i + 1][m - j]) {
flag = 1;break;
}
}
if(!flag) ans[++anss] = a[i];
}
// for(int i = 1;i <= n;++i) printf("%d %d\n",a[i],cnt[i]);
printf("%d\n",anss);
for(int i = 1;i <= anss;++i) printf("%d ",ans[i]);
return 0;
}
多重揹包
來一道例題:題目鏈接
題意:n經費,m種類的大米,每種大米有 金額p[i] 重量h[i] 以及最多的袋數c[i] 問在n經費內 時能得到的最大重量是多少?
做法:樸素的多重揹包
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e2+10;
int n,m,p[N],h[N],c[N],dp[N];
inline ll read()
{
ll x=0,w=1; char c=getchar();
while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
return w==1?x:-x;
}
int main()
{
int _=read();while(_--)
{
n=read(),m=read();
for(int i=1;i<=m;++i) p[i]=read(),h[i]=read(),c[i]=read();
for(int i=0;i<=n;++i) dp[i]=0;
for(int i=1;i<=m;++i){//枚舉種類
for(int j=1;j<=c[i];++j){//c[i]次的01揹包
for(int k=n;k>=p[i];--k){
dp[k]=max(dp[k],dp[k-p[i]]+h[i]);
}
}
}
printf("%d\n",dp[n]);
}
}
二進制優化多重揹包
這部分做法參考來自:wiki
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IO ios::sync_with_stdio(false)
#define pb push_back
#define mk make_pair
const int N = 1e5+10;
const int mod = 1e9+7;
int n;
int t[N], q[N], s[N];
int dp[2000];
int main(){
int h1, m1, h2, m2;
scanf("%d:%d %d:%d %d", &h1, &m1, &h2, &m2, &n);
if(m1 > m2){
m2 += 60; h2--;
}
int sumt = (h2-h1)*60 + m2 - m1;
for(int i = 0; i <= sumt; i++){
dp[i] = 0;
}
for(int i = 1; i <= n; i++){
scanf("%d%d%d", t+i, q+i, s+i);
}
int index = 0;
for(int i = 1; i <= n; i++){
int c = 1;
if(s[i] == 0 || s[i] >= sumt/t[i]) {
for(int j = t[i]; j <= sumt; j++){//普通的完全揹包
dp[j] = max(dp[j], dp[j-t[i]] + q[i]);
}
}
else{
while(s[i] - c > 0){//多重揹包的二進制優化
s[i] -= c;
for(int j = sumt; j >= c*t[i]; j--){
dp[j] = max(dp[j], dp[j-c*t[i]] + c*q[i]);
}
c *= 2;
}
if(s[i]){
for(int j = sumt; j >= s[i]*t[i]; j--){
dp[j] = max(dp[j], dp[j-s[i]*t[i]] + s[i]*q[i]);
}
}
}
}
printf("%d\n", dp[sumt]);
return 0;
}