01背包变式(跑步、精卫填海)
-
题意
题目链接:https://www.luogu.com.cn/problem/P1806 跑步
题目链接:https://www.luogu.com.cn/problem/P1510 精卫填海
01背包,就是每个物品只能拿一次,0 和 1, 拿或者不拿。
先讲一下原始版本,有 n 个物品,每个物品有一个体积 v,和价值 val,背包容量为 w ,问怎么拿使得总价值最大,且体积不超。
再讲一下这个变式。
跑步:就是甲准备在操场跑 n 圈,然后他跑步是分阶段的,比如说第一次跑 1 圈,然后第二次跑 2 圈,最后总共跑 n 圈。限制条件就是这次跑的圈数必须比上一次的圈数要多,假定第 0 次跑了 0 圈。问如果甲要跑 n 圈,有多少种方案?
转化成01背包 : 一开始我是没看出来,后面一看题解就悟了。首先圈数是递增的,也就是说从 1 到 n,每个数字只能出现一次,这不就是 01背包里每个物品只有拿或者不拿两种选择吗。不同的是这里拿的总价值有一个约束,必须是 n。然后就是写状态转移方程,f[i][j] += f[i-j][k]; f[i][j] 意思就是跑了 i 圈,然后最后一次跑了 j 圈。跑 j 圈之前一次跑了 k 圈。这就是转移方程。但是复杂度有点高。
优化一下,就是改成一维数组,f[j] += f[j - i]; 这也是跑了 j 圈,然后最后一圈跑了 i 圈。
精卫填海 : 精卫要叼石头填满一个区域体积为 w,石头还有 n 个,精卫叼这些石头所消耗的体力为 c,每块石头的体积为 v,精卫的体力为 k。问精卫把该区域填满后所剩下的最大体力,如果不可能填满则输出 Impossible。
转化成01背包 : 这很明显的01背包,不用转化了。只是说跟平常的01背包不一样。这里有三个变量都需要遍历,第一个就是遍历每个石头,第二个就是精卫的体力,第三个就是填了的体积。首先遍历每个石头是必须的,这不用说。再就是选体力还是体积了。如果选体积,那么数组就要存体力,而我们要求所剩下的最大体力。但是我们遍历石头的时候,每次都是减体力,而且最后输出也不好输出,总之就是不行,因为我一开始就是这么想的。而如果我们选体力的话,那就很好求了,数组里面存体积,每次更新的时候看一下体积是否能够填满该区域,如果能,那就更新最大体力值。最后输出也简单,直接输出就行。
-
代码
跑步
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<math.h>
#include<stack>
#include<map>
#include<list>
#include<unordered_set>
#include<unordered_map>
#define endl '\n';
//#define int long long;
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int n, m, k;
ll f[505];
void sovle(){
cin >> n;
f[0] = 1;
for (int i = 1; i <= n; i ++){
for (int j = n; j >= i; j --){
f[j] += f[j - i];
}
}
cout << f[n] - 1 << endl;
}
int main()
{
int t = 1;
//scanf("%d", &t);
while (t --){
sovle();
}
return 0;
}
精卫填海:
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<math.h>
#include<stack>
#include<map>
#include<list>
#include<unordered_set>
#include<unordered_map>
#define endl '\n';
//#define int long long;
using namespace std;
typedef long long ll;
const int N = 2e4+10;
int w, n, c, ans = -1;
int f[N], v[N], t[N];
void sovle(){
cin >> w >> n >> c;
for (int i = 1; i <= n; i ++){
cin >> v[i] >> t[i];
}
//f[w] = c;
for (int i = 1; i <= n; i ++){
for (int j = c; j >= t[i]; j --){
f[j] = max(f[j], f[j - t[i]] + v[i]);
if (f[j] >= w)ans = max(ans, c - j);
}
}
if (ans < 0){
cout << "Impossible" << endl;
}
else {
cout << ans << endl;
}
}
int main()
{
int t = 1;
//scanf("%d", &t);
while (t --){
sovle();
}
return 0;
}

浙公网安备 33010602011771号