[题目](https://www.luogu.org/problem/P1064)
这是一道不错的动态01背包的题目。
说起来我被背包问题整得很惨,在niop 2018 的时候就出了一道完全背包计数的题目货币系统,
可是那时,我才初步走上OI的道路,行程开始才五个月。水平比较低下。
但最气人的是我那时候不知怎么就是知道背包可以解决这类问题,就是不会写,day 1 T2 直接爆0。
唉,往事不堪回首。
现在进入正题。
这道题目是一道最优性问题,
最优性问题呢,据我了解,有两种方法
贪心 和 DP
而这道题目贪心显然行不通。
而且一看也知道这题是一个01背包。
那么废话了一堆,DP的转移方程怎么设计。
01背包最基本的方程
if(f[i]<f[i-v[i]]+w[i])
f[i]=f[i-v[i]]+w[i];
最麻烦的东西其实还是主件和附件的处理,
原则
1 有主件不一定有附件。
2有附件一定有主件。
那么新的方程就来了
if(第i件是主件&&f[i]<f[i-v[i]]+w[i])
f[i]=f[i-v[i]]+w[i];
if(第i件是附件&&在f[i-v[i]]的方案选择中有i的主件){
if(f[i]<f[i-v[i]]+w[i])
f[i]=f[i-v[i]]+w[i];
}
设i的主件的编号是x
if(第i件是附件&&在f[i-v[i]]的方案选择中没有i的主件){
int s=i-v[i]-v[x];
if(s>=0&&在f[s]的选择方案中没有物品i和x){
if(f[i]<f[s]+w[x]+w[i])
f[i]=f[x]+w[x]+w[i];
}
}
虽然方程出来了,但是还有一个问题没有解决;
就是如何判断f[i]的方案选择;
由于本题较水,多开一维就搞定了。
AC代码如下
#include<bits/stdc++.h>
using namespace std;
int n,m;
int f[32005][65],v[65],w[65],pd[65];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int k;
scanf("%d%d%d",&v[i],&k,&pd[i]);
w[i]=v[i]*k;
}
for(int i=1;i<=m;i++)
for(int j=n;j>=v[i];j--){
int p=pd[i];//主附件
if(f[j][i]) continue;
if(p==0&&f[j][0]<f[j-v[i]][0]+w[i]){
f[j][0]=f[j-v[i]][0]+w[i];
for(int k=1;k<=m;k++)
f[j][k]=f[j-v[i]][k];
f[j][i]=1;
}
if(p&&f[j-v[i]][p]&&f[j][0]<f[j-v[i]][0]+w[i]){
f[j][0]=f[j-v[i]][0]+w[i];
for(int k=1;k<=m;k++)
f[j][k]=f[j-v[i]][k];
f[j][i]=1;
}
int h=j-v[i]-v[p];
if(p&&h>=0&&!f[h][p]&&!f[h][i]){
if(f[j][0]<f[h][0]+w[i]+w[p]){
f[j][0]=f[h][0]+w[i]+w[p];
for(int k=1;k<=m;k++)
f[j][k]=f[h][k];
f[j][i]=1; f[j][p]=1;
}
}
}
cout<<f[n][0];
return 0;
}