DP
DP
1.背包
1. 01背包
这个山洞里有一些不同的草药,采每一株都需要一些时间,一株只能拿一次,每一株也有它自身的价值。在一段时间里,你可以采到一些草药。你应该可以让采到的草药的总价值最大。采药
特点:一个东西拿一次
dp方程:dp[i][j]表示考虑到前i个物品,占了j的体积所拿到的最大的价值,则
if(j>=v[i])dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
else dp[i][j]=dp[i-1][j];
如果可以拿,要么拿要么不拿. 如果不能拿那就不拿
ps:优化空间:发现dp[i][j]只与dp[i-1]有关 所以将方程变为dp[j].dp[j]=max(dp[j],dp[j-v[i]]+w[i])
for(int i=1;i<=n;++i)
for(int j=m;j>=v[i];--j)//要倒着来
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
2. 完全背包
这个山洞里有一些不同的草药,采每一株都需要一些时间,一株可以拿无数次,每一株也有它自身的价值。在一段时间里,你可以采到一些草药。你应该可以让采到的草药的总价值最大。疯狂的采药
特点:一个东西拿无数次
dp方程:dp[i][j]表示考虑到前i个物品,占了j的体积所拿到的最大的价值,则
for(int i=1;i<=n;++i)
for(int j=v[i];j<=m;++j)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
3. 分组背包
他的物品大致可分为 k 组,每组中的物品相互冲突,现在,他想知道最大的利用价值是多少。
特点:组内冲突
方法:先枚举组再枚举体积最后枚举每组里的每个东西
for(int i=1;i<=n;++i)
{
cin>>v[i]>>w[i]>>c[i];maxx=max(maxx,c[i]);
k[c[i]][++tot[c[i]]]=i;
}
for(int kk=1;kk<=maxx;++kk)
for(int j=0;j<=m;++j)
{
dp[kk][j]=dp[kk-1][j];
for(int i=1;i<=tot[kk];++i)
{
int id=k[kk][i];
if(j>=v[id])dp[kk][j]=max(dp[kk][j],dp[kk-1][j-v[id]]+w[id]);
}
}
cout<<dp[maxx][m];
4. 多重背包
特点:每个东西可以选k[i]次
dp[i][j]=dp[i-1][j];
for(int k=1;k*v[i]<=j;++k)//朴素暴力
dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
优化:二进制拆分
将k个这个东西分为2^0个,2^1个...2^k个,使这几个刚好可以组成k个东西,然后01背包
5. 附件背包
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、1 个或 2 个附件。每个附件对应一个主件,附件不再有从属于自己的附件。金明的预算方案
01背包的决策:
1.不选,然后去考虑下一个
2.选,背包容量减掉那个重量,总值加上那个价值。
这个题的决策是五个,分别是:
1.不选,然后去考虑下一个
2.选且只选这个主件
3.选这个主件,并且选附件1
4.选这个主件,并且选附件2
5.选这个主件,并且选附件1和附件2.
6. 混合背包
特点:又有01又有完全又有多重
for(int i=1;i<=n;++i)//简单粗暴
{
if(是01背包)for(int j=m;j>=v[i];j--)dp...
if(是完全背包)for(int j=v[i];j<=m;++j)dp...
}
7. 2维背包
特点:有两个费用
for(int i=1;i<=n;++i)
for(int j=0;j<=m1;++j)//费用1
for(int jj=0;jj<=m2;++jj)//费用2
dp[i][j][jj]=max(dp[i-1][j][jj],dp[i-1][j-v1[i]][jj-v2[i]]+w[i]);
2.线性DP
1.LIS 最长上升子序列
for(int i=1;i<=n;++i)//dp[i] 考虑前i个,第i个必选的LIS的长度
{
dp[i]=1;
for(int j=1;j<i;++j)
if(a[j]<a[i])dp[i]=max(dp[i],dp[j]+1);
}
O(n^2)
for(int i=1;i<=n;++i)//dp[i]:该序列中,上升子序列长度为i的上升子序列,的最小末尾数值
{
int now=0;
for(int step=(1<<20);step>=1;step/=2)
{
if(now+step<=n&&dp[now+step]<tong[i]&&dp[now+step]!=dp[0])
now+=step;
}
dp[now+1]=a[i];
}
O(nlogn)
2.LCS 最长公共子序列
dp[0][0]=1;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
{
if(a[i]==b[j]) dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
3.区间DP
eg:区间合并求分数石子合并
for(int len=1;len<=n*2;++len)
{
for(int l=1;l+len<=n*2;++l)
{
int r=l+len;
for(int k=l;k<r;++k)
{
dp[l][r]=max(dp[l][r],dp[l][k]+dp[k+1][r]+sum[r]-sum[l-1]);
dpp[l][r]=min(dpp[l][r],dpp[l][k]+dpp[k+1][r]+sum[r]-sum[l-1]);
}
}
}
套路:
for(int len=1;len<=n*2;++len)
{
for(int l=1;l+len<=n*2;++l)
{
int r=l+len;
for(int k=l;k<r;++k)
{
dp...
}
}
}