在这里插入图片描述
UVA10163
这道题需要两次dp

1. 求最小安全指数最大值

定义
$dp[i][j]$为考虑前i个人看守前j个仓库最小安全指数最大值,$SafetyIndex[i]$为第$i$个人的能力值。
初始化
$dp[i][0]=inf$(常数/0=无穷大)
剩下的$dp=0$
转移方程

其中$x$为前$i$个人看守前$x$个仓库,而剩下的$j-x$个仓库由第$i$个人看守。
这样,$dp[M][N]$就是所求答案。

2. 求此时的最小花费

定义
$dp[i][j]$为考虑前$i$个人看守前$j$个仓库并满足求出的安全指数条件的最小花费
初始化
$dp[0][i]=inf$
剩下的dp元素=0
转移方程

AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<map>
using namespace std;
constexpr static int inf = 0x3f3f3f3f;
int N, M;
int SafetyIndex[31];
int dp[31][101];
//考虑前i个人看守前j个仓库最小安全指数最大值
bool Input() {
cin >> N >> M;
if (!N && !M) {
return false;
}
for (int i = 1; i <= M; ++i) {
cin >> SafetyIndex[i];
}
return true;
}
void DP() {
//第一次dp
for (int i = 0; i <= M; ++i) {
dp[i][0] = inf;
}
for (int i = 1; i <= M; ++i) {
for (int j = 1; j <= N; ++j) {
dp[i][j] = dp[i - 1][j];
for (int x = 0; x < j; ++x) {
dp[i][j] = max(dp[i][j], min(dp[i - 1][x], SafetyIndex[i] / (j - x)));
}
}
}
int Min = dp[M][N];
cout << Min << ' ';
//如果安全指数为0,则谁都不雇佣最便宜
if (!Min) {
cout << 0 << endl;
return;
}
//第二次dp
memset(dp, 0x0, sizeof(dp));
for (int i = 1; i <= N; i++) {
dp[0][i] = inf;
}
for (int i = 1; i <= M; ++i) {
for (int j = 1; j <= N; ++j) {
dp[i][j] = dp[i - 1][j];
for (int x = 0; x < j; ++x) {
if (SafetyIndex[i] / (j - x) >= Min) {
dp[i][j] = min(dp[i][j], dp[i - 1][x] + SafetyIndex[i]);
}
}
}
}

cout << dp[M][N] << endl;
}
int main() {
while (Input()) {
memset(dp, 0x0, sizeof(dp));
DP();
}
return 0;
}

每次的$dp[i]$都只用到$dp[i-1]$,所以可以滚动优化降一维,过了那就算了吧233333。