问题
仓库守卫,一共有N个仓库,有M人应聘首位,每个仓库最多一个守卫,一个守卫可以看管多个仓库,守卫i的能力值是
,薪资等于能力值,仓库的安全系数L等于
的整数部分,
是守卫
看守的仓库数量
求最小安全系数的最大值,和此前提下的最小能力值总和
分析
和多重背包思路有点相似
分开计算,首先求最大最小安全系数 L(使用二分法或者DP),再求最小的能力值总和Y
状态:
L(i,j)代表为前i个守卫安排负责前j个仓库得到的最大最小安全系数
Y(i,j)代表为前i个守卫安排负责前j个仓库在符合要求的情况下(系数大于要求)得到的最小费用
状态转移方程是
#include<iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=105,maxm=35,Inf=0x3f3f3f3f;
int L[maxm][maxn],Y[maxm][maxn],N,M,p[maxn]; //L代表最大最小安全系数,Y代表此前提下的最小能力值总和
int main(void){
for(int i=0;i<maxm;++i){
L[i][0]=Inf; //i人看守0个仓库
}
//N是仓库数量,M是应聘人数
while(cin>>N>>M && N){
for(int i=1;i<=M;++i) scanf("%d",&p[i]);
for(int i=1;i<=M;++i){ //守卫
for(int j=1;j<=N;++j){ //仓库
L[i][j]=L[i-1][j]; //第i个守卫不雇佣
for(int k=1;k<=j;++k){ //雇佣第i个守卫看守k个仓库
L[i][j]=max(L[i][j],min(p[i]/k,L[i-1][j-k]));
}
}
}
int ans=L[M][N];
for(int i=1;i<=M;++i){
for(int j=1;j<=N;++j){
Y[i][j]=Inf;
if(L[i][j]<ans) break;
if(L[i-1][j]>=ans)
Y[i][j]=Y[i-1][j]; //第i个守卫不雇佣,费用不变
for(int k=1;k<=j && ans*k<=p[i];++k){ //雇佣第i个守卫看守k个仓库
if(L[i-1][j-k]>=ans)
Y[i][j]=min(Y[i][j],p[i]+Y[i-1][j-k]);
}
}
}
printf("%d %d\n",ans,Y[M][N]);
}
}
#include<iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=105,maxm=35,Inf=0x3f3f3f3f;
int L[maxn],Y[maxn],N,M,p[maxn]; //L代表最大最小安全系数,Y代表此前提下的最小能力值总和
int main(void){
//N是仓库数量,M是应聘人数
while(cin>>N>>M && N){
for(int i=1;i<=M;++i) scanf("%d",&p[i]);
memset(L,0,sizeof(L));
memset(Y,Inf,sizeof(Y));
L[0]=Inf;
for(int i=1;i<=M;++i){
for(int j=N;j>=1;--j){
for(int k=1;k<=j;++k){
L[j]=max(L[j],min(p[i]/k,L[j-k]));
}
}
}
int ans=L[N];
if(ans==0){ //这种写法无法处理一个守卫都不选的情况,只能输出0
printf("%d %d\n",ans,0);
continue;
}
Y[0]=0;
for(int i=1;i<=M;++i){
for(int j=N;j>=1;--j){
for(int k=1;k<=j && ans*k<=p[i];++k){
// if(Y[j-k]>=ans)
Y[j]=min(Y[j],p[i]+Y[j-k]);
}
}
}
printf("%d %d\n",ans,Y[N]);
}
}