简单问题
01背包
012背包
部分背包
机器分配
烽火传递
花店橱窗问题
简单问题
01背包
一个容量为m的背包,有n个物品,第i个物品的体积为wi,价值为ci。选择若干物品,使得体积总和不超过m的情况下价值总和最大。
n<=100,m<=10000。
搜索 复杂度为2^n
void dfs(
int x,
int y,
int z)
//当前物品编号,当前容量,当前价值
{
if(x==n+
1)
{
if(y<m)ans=
max(ans,z);
return;
}
dfs(x+
1,y+w[x],z+
c[i]);
dfs(x+
1,y,z);
}
如果前两维相同,只需选择第三维的最大值
小小的变形
const int INF=
0x7f;
int dfs(
int x,
int y)
{
int ans;
if(x==n+
1)
{
if(y>m)
return -
INF;
return 0;
}
ans=max(dfs(x+
1,y+w[x])+c[x],dfs(x+
1,y));
return ans;
}
dfs(1,
0);
加一个数组记录
const int INF=
0x7f;
int dfs(
int x,
int y)
{
if(x==n+
1)
{
if(y>m)
return -
INF;
return 0;
}
dp[x][y]=max(dfs(x+
1,y+w[x])+c[x],dfs(x+
1,y));
return dp[x][y];
}
dfs(1,
0);
应用记录状态 记忆化搜索 n*m
const int INF=
0x7f;
int dfs(
int x,
int y)
{
if(y>m)
return -
INF;
if(x==n+
1)
{
return 0;
}
if(dp[x+
1][y+w[x]])
//选该物品
dp[x][y]=max(dp[x+
1][y+w[x]]+
c[x],dp[x][y]);
else
dp[x][y]=max(dfs(x+
1,y+w[x])+
c[x],dp[x][y]);
if(dp[x+
1][y])
//不选该物品
dp[x][y]=max(dp[x+
1][y]+
c[x],dp[x][y]);
else
dp[x][y]=max(dfs(x+
1,y))+
c[x],dp[x][y]);
return dp[x][y];
}
dfs(1,
0);
递归变递推
先写出搜索
最大/最小值 存放到数组中
后面递归该状态时 直接使用数组中的值来代替
O(dfs里的状态个数*转移的时间复杂度)
找边界
找递推方法(顺序还是逆序)
转移(推转移方程)
搜索->记忆化搜索->递归变递推
for(
int i=n;i>=
1;i--
)
for(
int j=
0;j<=m;j++
)
dp[i][j]=max(dp[i+
1][j],dp[i+
1][j+w[i]]+
c[i]);
ans=dp[
1][
0]
012背包
一个容量为m的背包,有n个物品,第i个物品的体积为wi,价值为ci,有2个。
选择若干物品,使得体积总和不超过m的情况下价值总和最大。
记忆化搜索
const int INF=
0x7f;
int dfs(
int x,
int y,
int z)
{
if(x==n+
1){
if(y<m)ans=max(ans,z);
return -
INF;}
if(dp[x+
1][y])
//一个都不选
dp[x][y]=max(dp[x][y],dp[x+
1][y]);
else
dp[x][y]=max(dp[x][y],dfs(x+
1,y));
if(dp[x+
1][y+w[x]])
//选一个
dp[x][y]=max(dp[x][y],dp[x+
1][y+w[x]])+
c[x]);
else
dp[x][y]=max(dp[x][y],dfs(x+
1,y+w[x])+
c[x]);
if(dp[x+
1][y+w[x]])
//选两个
dp[x][y]=max(dp[x][y],dp[x+
1][y+
2*w[x]])+
2*
c[x]);
else
dp[x][y]=max(dp[x][y],dfs(x+
1,y+
2*w[x])+
2*
c[x]);
return dp[x][y];
}
dfs(1,
0);
部分背包
一个容量为m的背包,有n个物品,第i个物品的体积为wi,价值为ci,有ki个。
选择若干物品,使得体积总和不超过m的情况下价值总和最大。
记忆化搜索
int dfs(
int x,
int y)
{
if (y>m)
return -
INF;
if (x==n+
1)
return 0;
for (
int i=
0; i<=k[x]; i++
)
{
if (dp[x+
1][y+i*
w[x]])
dp[x][y]=max(dp[x][y],dp[x+
1][y+i*w[x]]+
c[x]);
else
dp[x][y]=max(dp[x][y],dfs(x+
1,y+i*w[x])+i*
c[x]);
}
return dp[x][y];
}
dfs(1,
0);
递推
for (i=n; i>=
1; i--
)
for (j=
0; j<=m; j++
)
for (l=
0; l<=k[i]; l++
)
dp[i][j]=max(dp[i][j],dp[i+
1][j+l*w[i]]+l*
c[i]);
dp[1][
0]
机器分配
有n家店,每家店都有m台机器,第i家店购买j台机器花费a[i][j]元(可能存在a[i][j]>a[i][j+1]),要购买总共m台机器,求最小花费。
搜索
//机器分配
dfs(
int x,
int y,
int z)
{
if(y>m)
return;
if(x==n+
1)
{
if(y==n+
1)ans=
min(ans,z);
return;
}
for(
int i=
0;i<=m;i++
)
dfs(x+
1;y+i,z+
a[x][i]);
}
dfs(1,
0,
0);
小小的变形
//机器分配
int dfs(
int x,
int y)
{
if(y>m)
return -
INF;
if(x==n+
1)
{
if(y==m)
return 0;
//再也不需要买机器
return -
INF;
}
for(
int i=
0;i<=m;i++
)
dp[x][y]=min(dp[x][y],dfs(x+
1,y+i)+
a[x][i]);
return dp[x][y];
}
dfs(1,
0);
记忆化搜索
//机器分配
int dfs(
int x,
int y)
{
if(y>m)
return -
INF;
if(x==n+
1)
{
if(y==m)
return 0;
//再也不需要买机器
return -
INF;
}
for(
int i=
0;i<=m;i++
)
if(dp[x+
1][y+
i])
dp[x][y]=min(dp[x][y],dp[x+
1][y+i]+
a[x][i]);
else
dp[x][y]=min(dp[x][y],dfs(x+
1,y+i)+
a[x][i]);
return dp[x][y];
}
dfs(1,
0);
递推
for(
int i=n;i>=
1;i--
)
for(
int j=
0;j<=m;j++
)
{
dp[i][j]=
INF;
for(k=
0;k<=m-j;k++
)
dp[i][j]=min(dp[i][j],dp[i+
1][j+k]+
a[i][k]);
}
ans=dp[
1][
0]
烽火传递
给定n个非负整数,选择其中若干数字,使得每连续k个数中至少有一个数被选出。
要求选择的数字之和尽可能小。
搜索
void dfs(
int x,
int y)
{
if(x==n+
1){ans=min(ans,y);
return;}
for(
int i=x+
1;i<=min(n+
1,x+k);i++
)
dfs(i,y+a[i]);
//i是下一个
}
dfs(0,
0);
记忆化
int dfs(
int x)
{
if(x==n+
1)
return 0;
dp[x]=
INF;
for(
int i=x+
1;i<=min(n+
1,x+k);i++
)
if(dp[i])dp[x]=min(dp[x],dp[i]+
a[i]);
else
dp[x]=min(dp[x],dfs(i)+
a[i]);
return dp[x];
}
dfs(0);
递推
/*dp[n+1]=0;
dp[1] dp[2] dp[3] .. dp[1+k]*/
for (i=n; i>=
0; i--
)
{
dp[i]=
INF;
for (
int j=i+
1; j<=min(n+
1,i+k); j++
)
dp[i]=min(dp[i],dp[j]+
a[j]);
}
dp[0]
花店橱窗问题
给定一个n*m的矩阵A(n<=m),求一个序列a1,a2,…,an满足1<=a1<a2<…<an<=m。使得A[1][a1]+A[2][a2]+…+A[n][an]最大。A可能有负数。
搜索
void dfs(
int x,
int y,
int z)
{
if(x==n+
1)
{
if(y==m+
1)ans=
max(ans,z);
return;
}
for(
int i=y+
1;i<=m;i++
)
dfs(x+
1,i,z+a[x+
1][i]);
}
dfs(0,
0,
0);
记忆化搜索
int dfs(
int x,
int y)
{
if(x==n+
1)
{
if(y==m+
1)
return 0;
else return -
INF;
}
for(
int i=y+
1;i<=m;i++
)
{
if(dp[x+
1][i])
dp[x][y]=max(dp[x][y],dp[x+
1][i]+a[x+
1][i]);
else
dp[x][y]=max(dp[x][y],dfs(x+
1,i)+a[x+
1][i]);
return dp[x][y];
}
}
dfs(0,
0);
递推
/*dp[n+1][m+1]=0; dp[n+1][0~m]=-INF;
dp[1][] dp[2][] dp[3][]*/
for (i=n; i>=
0;i--
)
for (j=
0; j<=m+
1; j++
)
for (k=j+
1; k<=m+
1; k++
)
dp[i][j]=max(dp[i][j],dp[i+
1][k]+a[i+
1][k]);
cout<<dp[
0][
0];
转载请注明原文地址: https://ju.6miu.com/read-672246.html