题意:
有n个仓库,现在要请m个保安来看守.
每个保安有个能力值Pi,Pi值为多少,这个保安最多就能看守多少个仓库.同时也是聘请这个保安的工资.
如果这个保安看守k个仓库,那么每个仓库的安全系数就是Pi / k (整除);如果一个仓库没有保安看守,那么安全系数就是0;总安全系数L 就是所有仓库中安全系数最低的那个仓库的值.
现在我们要求L最大是多少,并且求L最大情况下,最小的工资Y;
思路:
这题可以用两个dp来解,第一个求L,第二个求Y;
首先我们要先求L,
f[i][j]表示前i个保安,看守前j个仓库,最小的安全系数 最大是多少;
我们可以用到一个状态转移
f[cur][l] = max(f[cur][l] , min(dp(cur - 1 , l - i) , p[cur] / i)); // (i <= l)
min(dp(cur - 1 , l - i) , p[cur] / i )表示当前这个保安看守i个仓库,(那么这个保安看守的仓库的安全值就是p[cur] / i) 和之前的cur - 1个保安看守l - i个仓库的安全系数取小值.
并且在所有小值中取最大值.
这样就得到了最大的安全系数.
在知道安全系数的情况下,我们再来求最小工资.
f2[i][j]表示前i个保安,看守前j个仓库(所有安全系数都满足条件 >L),最小工资是多少.
f2[cur][l] = min(f2[cur][l] , dp2(cur - 1 , l - i) + p[cur]); // i <= l && p[cur] / i >= L
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 105;
const int M = 35;
int n,m,L, Y ,p[N];
int f[M][N];
int f2[M][N];
int dp(int cur , int l) {
if(cur == 0) {
if(l > 0)
return 0;
return INF;
}
if(l == 0) {
return INF;
}
if(f[cur][l] != -1)
return f[cur][l];
f[cur][l] = dp(cur - 1 , l);
for(int i = 1 ; i <= p[cur] ; i++) {
if(i <= l)
f[cur][l] = max(f[cur][l] , min(dp(cur - 1 , l - i) , p[cur] / i));
}
return f[cur][l];
}
int dp2(int cur , int l) {
if(cur == 0) {
if(l > 0)
return INF;
return 0;
}
if(l == 0)
return 0;
if(f2[cur][l] != -1)
return f2[cur][l];
f2[cur][l] = dp2(cur - 1 , l);
for(int i =1 ; i <= p[cur] ; i++) {
if(i <= l && p[cur] / i >= L)
f2[cur][l] = min(f2[cur][l] , dp2(cur - 1 , l - i) + p[cur]);
}
return f2[cur][l];
}
int main() {
while(scanf("%d%d",&n,&m) && n) {
memset(f, -1 ,sizeof(f));
memset(f2, -1 ,sizeof(f2));
for(int i = 1 ; i <= m ;i++) {
scanf("%d",&p[i]);
}
L = dp(m,n);
if(L != 0)
Y = dp2(m,n);
else
Y = 0;
printf("%d %d\n",L, Y);
}
}