动态规划
一、背包问题
一、01背包
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
(
1
)
F
[
i
,
j
]
:只在前
i
个物品里选,且总体积不超过
j
的最大价值
(
2
)
不选第
i
个:
F
[
i
−
1
]
[
j
]
(
3
)
选第
i
个:
F
[
i
−
1
]
[
j
−
v
[
i
]
]
+
w
[
i
]
(
4
)
F
[
i
]
[
j
]
=
m
a
x
(
F
[
i
−
1
]
[
j
]
,
F
[
i
−
1
]
[
j
−
v
[
i
]
]
+
w
[
i
]
)
\begin{align} &(1)F[i,j]:只在前i个物品里选,且总体积不超过j的最大价值\\ &(2)不选第i个:F[i-1][j]\\ &(3)选第i个 :F[i-1][j-v[i]]+w[i]\\ &(4)F[i][j]=max(F[i-1][j],F[i-1][j-v[i]]+w[i]) \end{align}
(1)F[i,j]:只在前i个物品里选,且总体积不超过j的最大价值(2)不选第i个:F[i−1][j](3)选第i个:F[i−1][j−v[i]]+w[i](4)F[i][j]=max(F[i−1][j],F[i−1][j−v[i]]+w[i])
优化前:
memset(f,0,sizeof f);
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
f[i][j]=f[i-1][j]; //不选
if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]); //选
}
cout<<f[n][m]<<endl;
优化后:(每次只会用到上一层的状态)
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1010;
int n,m;
int f[N];
int v[N],w[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
memset(f,0,sizeof f);
for(int i=1;i<=n;i++)
for(int j=m;j>=v[i];j--)
{
f[j]=max(f[j],f[j-v[i]]+w[i]); //选
}
cout<<f[m]<<endl;
return 0;
}
二、完全背包问题
有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
(
1
)
F
[
i
,
j
]
:只在前
i
个物品里选,且总体积不超过
j
的最大价值
(
2
)
选
k
(
k
>
=
0
)
个物品
i
:
F
[
i
−
1
]
[
j
−
k
∗
v
[
i
]
]
+
K
∗
w
[
i
]
\begin{align} &(1)F[i,j]:只在前i个物品里选,且总体积不超过j的最大价值\\ &(2)选k(k>=0)个物品i:F[i-1][j-k*v[i]]+K*w[i]\\ \end{align}
(1)F[i,j]:只在前i个物品里选,且总体积不超过j的最大价值(2)选k(k>=0)个物品i:F[i−1][j−k∗v[i]]+K∗w[i]
优化前:
for(int i = 1 ; i<=n ;i++)
for(int j = 0 ; j<=m ;j++)
{
for(int k = 0 ; k*v[i]<=j ; k++)
f[i][j] = max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
}
优化:
f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w , f[i-1,j-2*v]+2*w , f[i-1,j-3*v]+3*w , .....)
f[i , j-v]= max( f[i-1,j-v] , f[i-1,j-2*v] + w , f[i-1,j-3*v]+2*w , .....)
由上两式,可得出如下递推关系:
f[i][j]=max(f[i,j-v]+w , f[i-1][j])
for(int i = 1 ; i <= n ; i++)
for(int j = 0 ; j <= m ; j ++)
{
f[i][j] = f[i-1][j];
if(j-v[i]>=0)
f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]);
}
优化变一维:
for(int i = 1 ; i<=n ;i++)
for(int j = v[i] ; j<=m ;j++) 注意了,这里的j是从小到大枚举,和01背包不一样
{
f[j] = max(f[j],f[j-v[i]]+w[i]);
}
三、多重背包问题
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
思路1:参考完全背包
int n,m;
int v[N],w[N],s[N];
int f[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i]>>s[i];
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=s[i]&&k*v[i]<=j;k++)
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
cout<<f[n][m]<<endl;
return 0;
}
思路2**:二进制优化将n种物品,每种Si个的多重背包,拆分打包看成N件物品(组)的01背包问题**
(
1
)
例如当
s
[
i
]
=
7
时
(
2
)
将其拆分为:
1
,
2
,
4
的三组
,
就可以凑出
0
−
7
中的任意数
(
3
)
s
[
i
]
=
9
时:
1
,
2
,
4
,
2
就可以凑出
0
−
9
中的任意数
\begin{align} &(1)例如当s[i]=7时\\ &(2)将其拆分为:1,2,4的三组,就可以凑出0-7中的任意数\\ &(3)s[i]=9时:1,2,4,2就可以凑出0-9中的任意数 \end{align}
(1)例如当s[i]=7时(2)将其拆分为:1,2,4的三组,就可以凑出0−7中的任意数(3)s[i]=9时:1,2,4,2就可以凑出0−9中的任意数
#include<iostream>
using namespace std;
const int M=12000;
int n,m;
int f[M],v[M],w[M];
int main()
{
cin>>n>>m;
int cnt=0;
while(n--)
{
int a,b,c;
cin>>a>>b>>c;
int t=1;
while(c>=t)
{
v[++cnt]=a*t;
w[ cnt]=b*t;
c-=t;
t=t*2;
}
if(c) {v[++cnt]=a*c; w[cnt]=b*c;}
}
n=cnt;
//转化为了01背包
for(int i=1;i<=n;i++)
for(int j=m;j>=v[i];j--)
f[j]=max(f[j],f[j-v[i]]+w[i]);
cout<<f[m]<<endl;
return 0;
}
四、分组背包问题
有 N 组物品和一个容量是 V 的背包。
每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j是组内编号。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
(
1
)
F
[
i
,
j
]
:只在前
i
组里选,且总体积不超过
j
的最大价值
(
2
)
不选:
F
[
i
−
1
]
[
j
]
(
3
)
选
i
组的第
k
个:
F
[
i
−
1
]
[
j
−
v
[
i
]
[
k
]
]
+
w
[
i
]
[
k
]
\begin{align} &(1)F[i,j]:只在前i组里选,且总体积不超过j的最大价值\\ &(2)不选:F[i-1][j]\\ &(3)选i组的第k个:F[i-1][j-v[i][k]]+w[i][k] \end{align}
(1)F[i,j]:只在前i组里选,且总体积不超过j的最大价值(2)不选:F[i−1][j](3)选i组的第k个:F[i−1][j−v[i][k]]+w[i][k]
#include<iostream>
using namespace std;
const int N=110;
int n,m;
int s[N],v[N][N],w[N][N];
int f[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>s[i];
for(int j=0;j<s[i];j++)
cin>>v[i][j]>>w[i][j];
}
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
f[i][j]=f[i-1][j]; //不选
for(int k=0;k<s[i];k++)
{
if(j>=v[i][k]) f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]); //选第k个
}
}
cout<<f[n][m]<<endl;
return 0;
}