「
「
「动态规划
」
」
」第
1
1
1章 背包问题
(
(
(前
3
3
3题
)
)
)
目录:
A.采药问题
B.货币系统
C.宝物筛选
A . A. A. 例题 1 1 1 采药问题
分析:
这样的屑题真的需要分析吗
CODE:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=2005;
int w[N],c[N],f[N][N];
int main(){
int m,n;
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&w[i],&c[i]);
for(register int i=1;i<=n;i++)
for(register int j=1;j<=m;j++){
if(j>=w[i]) f[i][j]=max(f[i-1][j-w[i]]+c[i],f[i-1][j]); //屑2维01背包
else f[i][j]=f[i-1][j];
}
printf("%d",f[n][m]);
return 0;
}
B . B. B. 例题 2 2 2 货币系统
分析:
某凯的疑惑
样例:
3
3
3
19
19
19
10
10
10
6
6
6
19
19
19和
6
6
6都会被去掉 因为
19
=
10
+
3
+
3
+
3
19=10+3+3+3
19=10+3+3+3
,
,
,
6
=
3
+
3
6=3+3
6=3+3
剩下的就是
a
n
s
ans
ans了 就可以考虑一个类似桶的
d
p
dp
dp
f
i
f_i
fi表示
i
i
i最多由多少数拼成 那
f
i
>
1
f_i>1
fi>1就会被去掉
(
(
(可以由
2
2
2个及以上数拼成 就不需要保留
)
)
)
最后一个____转移:
f
j
=
m
a
x
(
f
j
,
f
j
−
a
i
+
1
)
f_j=max(f_j,f_{j-a_i}+1)
fj=max(fj,fj−ai+1)
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<bitset>
//#pragma GCC optimize(2)
#define reg register
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
int T,n,a[105],f[25005],ans;
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
ans=0;
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++)
for(int j=a[i];j<=25000;j++)
{
if(j==a[i]||f[j-a[i]])
f[j]=max(f[j],f[j-a[i]]+1);
}
for(int i=1;i<=n;i++)
if(f[a[i]]<=1) ans++;
printf("%d\n",ans);
}
return 0;
}
C . C. C. 例题 3 3 3 宝物筛选
分析:
多重背包 但没完全多重背包
m
<
=
1
0
5
m<=10^5
m<=105 会
T
L
E
TLE
TLE
考虑优化 可以二进制拆分 也可以单调队列优化
w
w
w表示重量
v
v
v表示价值
c
c
c表示数量 普通转移:
f
i
,
j
=
m
a
x
(
f
i
−
1
,
j
−
w
×
k
+
v
×
k
)
,
∑
k
=
1
c
f_{i,j}=max(f_{i-1,j-w\times k}+v\times k),\sum_{k=1}^{c}
fi,j=max(fi−1,j−w×k+v×k),∑k=1c
单调队列优化转移:
f
i
,
j
=
m
a
x
(
f
i
−
1
,
w
×
k
+
q
−
v
×
k
)
+
v
×
n
,
∑
k
=
1
c
f_{i,j}=max(f_{i-1,w\times k+q}-v\times k)+v\times n,\sum_{k=1}^{c}
fi,j=max(fi−1,w×k+q−v×k)+v×n,∑k=1c
其中
q
=
j
q=j
q=j
m
o
d
mod
mod
w
w
w
,
,
,
n
=
j
/
w
n=j/w
n=j/w
,
,
,
n
−
k
<
=
c
n-k<=c
n−k<=c
然后枚举余数 q q q 用单调队列优化每个 q q q
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<bitset>
//#pragma GCC optimize(2)
#define reg register
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=4e4+5;
int n,W,ans,head,tail,que[N],num[N],f[N];
int main()
{
scanf("%d%d",&n,&W);
for(int i=1;i<=n;i++)
{
int v,w,m;
scanf("%d%d%d",&v,&w,&m);
if(w==0){
ans+=v*m; //没有重量 就直接+总价值
continue;
}
int k=W/w;
m=min(m,k);
for(int yu=0;yu<w;yu++)
{
head=tail=0;
k=(W-yu)/w;
for(int j=0;j<=k;j++)
{
while(head<tail&&f[j*w+yu]-j*v>=que[tail-1])
tail--;
num[tail]=j;
que[tail++]=f[j*w+yu]-j*v;
while(head<tail&&num[head]<j-m)
head++;
f[j*w+yu]=max(f[j*w+yu],que[head]+j*v); //最终的转移方程
}
}
}
printf("%d",ans+f[W]);
return 0;
}