题目大意:给出m为仓库的数量, 给出n为有守夜人的数量, 然后给出n个数值,为对应守夜人应付的酬劳,每个守夜人的能力与他需要的酬劳是相等的,并且守夜人可以同时负责多个仓库的安全,不过这样子安全值就变为val[i]/k(val[i]表示第i个守夜人的能力值,k表示他负责的仓库数量, /为取整),先在要的出方案,使得所有仓库中安全值最低的那个仓库的安全值越高, 并且要使得酬劳越低。
解题思路:问题应该分成两个子问题来求解,先求安全值最大为多少,然后通过求出的安全值去求解酬劳的最优方案。
1、dp[j] = max(dp[j], min(dp[j - k], val[i] / k)), 可以将max中的两个数值看成是两个方案,是否用min中的方案取代原先的方案。 而min则为该方案中的安全值最低值。(dp[i] 表示保证了i个仓库的安全后的安全值最低值)
2、dp[j] = min(dp[j], dp[j - k] + val[i]), dp[j]表示保证j个仓库的安全值大于ans(第一步求出的最优解)时的花费最小值。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int INF = 0x3f3f3f3f; int main() { int n, m; while (scanf("%d%d", &n, &m), n) { int p[1010], DP[1010][110]; for (int i = 1; i <= m; i++) scanf("%d", &p[i]); memset(DP, 0, sizeof(DP)); for (int i = 1; i <= m; i++) { DP[i-1][0] = INF; for (int j = 1; j <= n; j++) { DP[i][j] = DP[i-1][j]; for (int k = 1; k <= j; k++) DP[i][j] = max(DP[i][j], min(DP[i-1][j-k], p[i] / k)); } } int L = DP[m][n]; memset(DP, INF, sizeof(DP)); for (int i = 1; i <= m; i++) { DP[i-1][0] = 0; for (int j = 1; j <= n; j++) { DP[i][j] = DP[i-1][j]; for (int k = 1; k <= j; k++) if (p[i] / k >= L) DP[i][j] = min(DP[i][j], DP[i-1][j-k] + p[i]); } } printf("%d %d\n", L, L ? DP[m][n] : 0); } return 0; }