题目大意:有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; }
时间: 2024-10-09 14:27:12