题意:
现在有 N 头奶牛, 每头奶牛的能量是E, 现在要奶牛们只要有一头完成跑D圈就完成任务。
但是每次都要由一头奶牛领跑, 没有能量的奶牛可以退场或继续跟跑, 但是带头的奶牛体力消耗是: x*x laps/min, 跟跑的是x laps/min。
现在要你计算出最短时间内完成任务。 (黑书 158 奶牛转圈)
思路:
1. dp[i][d][e] 表示第 i 只奶牛跑了 d 圈且能量消耗为 e 时,所需的最小分钟;
2. 针对第 i 只奶牛是否领跑,又展开了讨论,如果不领跑,注意到一个性质:跑了多少圈就消耗多少能量,则有: dp[i][d][d] = dp[i-1][d][e];
3. 如果第 i 只奶牛领跑,则考虑它领跑的速度是多少以及这 d 圈有多少圈是领跑的,可以利用完全背包的策略把代码简化为 O(N*D*E);
dp[i][d][] = min(dp[i][d][], dp[i][d-k][] + 1); 相当于体积为 1 的物品价值为 1,看成完全背包,时间规模减少一维;
4. 一开始我把转移方程式理解成了:dp[i][e][d] 表示第 i 只奶牛消耗能量值为 e 时且跑了 d 圈,所需的最小分钟,没有利用到完全背包的性质,见代码2 ;
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 110;
const int INFS = 0x3fffffff;
int dp[25][MAXN][MAXN], N, E, D;
int main() {
while (~scanf("%d%d%d", &N, &E, &D)) {
for (int i = 1; i <= N; i++)
for (int d = 0; d <= D; d++)
for (int e = 0; e <= E; e++)
dp[i][d][e] = INFS;
dp[1][0][0] = 0;
for (int i = 1; i <= N; i++) {
for (int d = 1; d <= D; d++) {
for (int e = 1; e <= E; e++) {
for (int k = 1; k*k <= e && k <= d; k++)
dp[i][d][e] = min(dp[i][d][e], dp[i][d-k][e-k*k] + 1);
dp[i+1][d][d] = min(dp[i+1][d][d], dp[i][d][e]);
}
}
}
int ans = INFS;
for (int e = 1; e <= E; e++)
ans = min(ans, dp[N][D][e]);
printf("%d\n", ans);
}
return 0;
}
代码2 (110ms) :
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 110;
const int INFS = 0x3fffffff;
int dp[25][MAXN][MAXN], N, E, D;
int main() {
while (~scanf("%d%d%d", &N, &E, &D)) {
for (int i = 1; i <= N; i++)
for (int e = 0; e <= E; e++)
for (int d = 0; d <= D; d++)
dp[i][e][d] = INFS;
dp[1][0][0] = 0;
for (int i = 1; i <= N; i++) {
for (int e = 1; e <= E; e++) {
for (int d = 1; d <= D; d++) {
for (int x = 1; ; x++) {
int coste = x * x, costd = x;
if (coste > e || costd > d) break;
for (int k = 1; k*coste <= e && k*costd <= d; k++)
dp[i][e][d] = min(dp[i][e][d], dp[i][e-coste*k][d-costd*k] + k);
}
dp[i+1][d][d] = min(dp[i+1][d][d], dp[i][e][d]);
}
}
}
int ans = INFS;
for (int i = 1; i <= N; i++)
for (int e = 1; e <= E; e++)
ans = min(ans, dp[i][e][D]);
printf("%d\n", ans);
}
return 0;
}