题目大意:有N(1<=N<=100)个仓库需要看管,有M(1<=M<=30)名应聘者,每个人有能力属性Pi(1<=Pi<=1000)。所有仓库都是一样的,每个仓库只能被一人看守,一人可看守多个仓库,当一人看守u个仓库时,每个仓库的安全度为Uj=Pi/u,总安全度为min Uj。雇佣一个能力值为Pi的人需要花费Pi元。求最大的总安全度,和在这样的情况下的最小花费。
先dp一次,求出可能的最大总安全度max,再次dp,求出在安全度为max下的最小花费。
第一次dp:用d[i][j]表示用i个人看守j个仓库能搭成的最大总安全度,用a[i]表示第i个人的能力值。
状态转移方程:d[i][j]=max { d[i-1][j],min { d[i-1][j-u],a[i]/u } }(a[i]/u>d[i-1][j] && u>0)
第二次dp:用d[i][j]表示用i个人看守j个仓库且总安全度不小于max时的最小花费,可用贪心思想优化,只取a[i]>=max的人,在dp时对每个人直接考虑他可以管的最多的仓库(a[i]/max)。
状态转移方程:d[i][j]=min { d[i-1][j],d[i-1][j-a[i]/max]+a[i] }
#include<stdio.h>
#include<stdlib.h>
int a[50];
int d[50][110];
int co[50][110];
int main(void)
{
int i,j,u,v,p,n,m,minp,max;
scanf("%d%d",&n,&m);
while((n!=0)||(m!=0))
{
for(i=1;i<=m;i++)
{
scanf("%d",&a[i]);
}
for(i=1;i<m;i++)
{
for(j=m-1;j>=i;j--)
{
if(a[j]<a[j+1])
{
p=a[j];
a[j]=a[j+1];
a[j+1]=p;
}
}
}
for(i=1;i<=n;i++)
{
d[1][i]=a[1]/i;
}
d[1][0]=10000000;
for(i=2;i<=m;i++)
{
d[i][0]=10000000;
for(j=1;j<=n;j++)
{
u=d[i-1][j];
for(v=1;v<=j;v++)
{
if(a[i]/v<=u)
{
break;
}
if(a[i]/v>d[i-1][j-v])
{
minp=d[i-1][j-v];
}
else
{
minp=a[i]/v;
}
if(minp>u)
{
u=minp;
}
}
d[i][j]=u;
}
}
max=d[m][n];
if(max==0)
{
printf("0 0\n");
}
else
{
p=m;
for(i=1;i<=m;i++)
{
if(a[i]<max)
{
p=i-1;
break;
}
}
for(i=1;i<=n;i++)
{
if(a[1]/i>=max)
{
co[1][i]=a[1];
}
else
{
co[1][i]=10000000;
}
}
for(i=2;i<=p;i++)
{
for(j=1;j<=n;j++)
{
minp=co[i-1][j];
u=a[i]/max;
if(u>=j)
{
if(a[i]<minp)
{
minp=a[i];
}
}
else
{
if(co[i-1][j-u]+a[i]<minp)
{
minp=co[i-1][j-u]+a[i];
}
}
co[i][j]=minp;
}
}
printf("%d %d\n",max,co[p][n]);
}
scanf("%d%d",&n,&m);
}
return 0;
}