题解 - Birds
题目大意
一条直线上有 \(n\) 棵树,第 \(i\) 棵树上有 \(c_i\) 只鸟。
在第 \(i\) 棵树下召唤一只鸟的魔力代价是 \(cost_i\)。每召唤一只鸟,魔力上限会增加 \(B\)。每向前走一棵树,会增加 \(X\) 的魔力。一开始的魔力和魔力上限都是 \(W\)。你只能向前移动。
问最多能够召唤几只鸟。
思路简析
还是先考虑状态:必然不能以魔力为状态,那么只能以树和鸟,而答案却又是树和鸟所以我们需要反向思考一下:\(f_{i, j}\) 代表在第 \(i\) 棵树时总共已召唤了 \(j\) 只鸟时能有的最大魔力值,
最终结果是 \(\max\limits_{f_{n, j}\ is\ existent}\{j\}\)。
转移方程:
(\(Range: i_{1\rightarrow n}, j_{0\rightarrow\sum\limits_{p=0}^i c_p}\) )
这里的 \(k\) 表示在第 \(i\) 棵树下召唤了 \(k\) 只鸟;加上的 \(X\) 是从第 \(i\) 棵树走到第 \(i+1\) 棵树的魔力值,所以严格来说其实当 \(i=n\) 时不应加上 \(X\),但是有判断条件即可;转入该方程应有条件:\(f_{i-1, j-k}-cost_i\times k\)(这里就是不加 \(X\))。
边界条件是 \(f_{0, 0} = W\)。
那么就比较简单了。
这真的是背包 dp 吗?
另外考虑你的魔力上限,显然是 \(W+B\times j\)。
我是真服了,\(cst_i \times k\) 会爆 int
,硬控我 4h+。
完了到最后好像是被盾了。
这东西虽然是三层循环,但是似乎时间复杂度仅是 \(\Omega(n\cdot\sum\limits_{i=1}^nc_i)\) 即 \(10^7\) 的(若算错请指正谢谢。)。
点击查看代码
#include <bits/stdc++.h>
#include <bits/extc++.h>
namespace {
using namespace std;
using namespace __gnu_pbds;
#define fiin(x) freopen(x".in", "r", stdin)
#define fiout(x) freopen(x".out", "w", stdout)
#define files(x) fiin(x), fiout(x)
#define und unsigned
#define ll long long
#define db double
#define pii pair<int, int>
#define mp(x, y) make_pair(x, y)
#define m1p(x, y) ((x<<14)+y)
#define fir first
#define sec second
#define hap gp_hash_table
// #define pri_que
const int man = 1e3+10, mac = 1e4+10;
}
ll n, W, B, X;
ll c[man], cst[man], sum[man];
ll f[man][mac];
int main () {
// files("test");
scanf("%lld%lld%lld%lld", &n, &W, &B, &X);
for (int i = 1; i <= n; ++ i) scanf("%lld", c+i), sum[i] = sum[i-1]+c[i];
for (int i = 1; i <= n; ++ i) scanf("%lld", cst+i);
memset(f, -1, sizeof(f)), f[0][0] = W;
for (int i = 1; i <= n; ++ i)
for (int j = 0; j <= sum[i]; ++ j) {
for (int k = 0; k <= min(1LL*j, c[i]); ++ k)
if (f[i-1][j-k]-cst[i]*k>=0)
f[i][j] = max(f[i][j], f[i-1][j-k]+X-cst[i]*k);
f[i][j] = min(f[i][j], W+B*j); }
for (int i = sum[n]; i; -- i)
if (f[n][i] != -1) return printf("%d", i)&0;
return puts("0")&0;
}
点名批评可爱洛天依,写 \(\max\) 不写范围,中间变量还来回标错。