通天之潜水
题目背景
直达通天路·小 A 历险记第三篇
题目描述
在猴王的帮助下,小 A 终于走出了这篇荒山,却发现一条波涛汹涌的河拦在了自己的面前。河面上并没有船,但好在小 A 有 n n n 个潜水工具。由于他还要背重重的背包,所以他只能背 m m m 重的工具,又因为他的力气并不是无限的,河却很宽,所以他只能背有 v v v 阻力的工具。但是这条河下有非常重要的数据,所以他希望能够停留的时间最久。于是他找到了你,让你告诉他方案。
输入格式
三个数 m , v , n m, v, n m,v,n 如题目所说。
接下来 n n n 行,每行三个数 a i , b i , c i a_i, b_i, c_i ai,bi,ci 分别表示所含的重力,阻力,能够支撑的时间。
输出格式
第一行一个数,表示最长的时间。
接下来一行,若干个数,表示所选的物品。
样例 #1
样例输入 #1
100 100 3
50 60 289
40 10 116
50 50 106
样例输出 #1
405
1 2
提示
对于 100 % 100 \% 100% 的数据, 1 ≤ m , v ≤ 200 1 \le m, v \le 200 1≤m,v≤200, 1 ≤ n ≤ 100 1 \le n \le 100 1≤n≤100。
数据保证一定有方案。
若有多种方案,输出前面尽量小的方案。
看起来是个很简单的二维费用背包是吧,只要模仿01背包的做法就可以了。
这个题为什么是绿色的?还需要输出选择物品的方案,而且题目会卡,不优化空间就会MLE,因此我们需要在滚动数组/二维数组(二位费用背包本来是三维的)的情况下,输出选择物品的方案。
为了循序渐进,先考虑三维状态的背包是如何输出选择物品的方案的,每个状态[物品数][费用1][费用2]存一个vector,加上这个vector,这个数组是四维数组了,当然网上看到有用char存的,如果不用vector改用char或许不需要优化空间直接就过了。我们需要在每次发现可以更新的时候,把子问题的物品选择方案复制一份过来,然后把当前物品的编号放进后面去。
注意:每个状态只更新了一次,因为循环只会在这个状态停留一次,那么在更新/沿用i-1的时候,只会这样被处理一次物品选择的方案,不会反复修改。换句话说,每个状态的物品选择方案,一旦更新就肯定是**使得价值最大(但是对于同样的最大价值,方案可能不唯一,首次选择用哪种方案,并不确定,需要修改代码细节来控制,详见下文)**的方案。
既然每个状态只更新一次,那么选择的“路径”是无后效性的,可以边递推边存储路径。
但是这个题真的值绿色吗? 毫无疑问我这样做hack都没过, 注意到,题目的最后,写明了要输出的方案要求前面的物品尽可能编号小,用hack1的数据试一下你就懂了
先上我的没AC的代码:
#include<iostream>
#include<vector>
using namespace std;
int m,v,n,a[101],b[101],c[101];
int f[201][201];
vector<int> items[201][201];
int main()
{
cin>>m>>v>>n;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i]>>c[i];
for(int i=1;i<=n;i++)
{
for(int j=m;j>=a[i];j--)
{
for(int k=v;k>=b[i];k--)
{
if(f[j-a[i]][k-b[i]]+c[i]>f[j][k])
{
f[j][k]=f[j-a[i]][k-b[i]]+c[i];
items[j][k]=items[j-a[i]][k-b[i]];
items[j][k].push_back(i);
}
else items[j][k]=items[j][k];
}
}
}
cout<<f[m][v]<<endl;
for(int i=0;i<items[m][v].size();i++)
{
cout<<items[m][v][i]<<" ";
}
return 0;
}
分析可知我的代码的更新条件是“惰性”的,必须要严格增才会更新,也就是说,我的代码会使得最后一个物品的编号尽可能小
如果加了等于号,那么会使得最后一个物品的编号尽可能大
敏锐的你一定会察觉到,只要把物品的顺序反过来,那么就变成了第一个物品的编号尽可能小,最后再反序输出就得到正确顺序的答案了
AC代码如下:
#include<iostream>
#include<vector>
using namespace std;
int m,v,n,a[101],b[101],c[101];
int f[201][201];
vector<int> items[201][201];
int main()
{
cin>>m>>v>>n;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i]>>c[i];
for(int i=n;i>=1;i--)
{
for(int j=m;j>=a[i];j--)
{
for(int k=v;k>=b[i];k--)
{
if(f[j-a[i]][k-b[i]]+c[i]>=f[j][k])
{
f[j][k]=f[j-a[i]][k-b[i]]+c[i];
items[j][k]=items[j-a[i]][k-b[i]];
items[j][k].push_back(i);
}
else items[j][k]=items[j][k];
}
}
}
cout<<f[m][v]<<endl;
for(int i=items[m][v].size()-1;i>=0;i--)
{
cout<<items[m][v][i]<<" ";
}
return 0;
}