M M 个管理员来应聘,第ii个人的能力值为pi"role="presentation"st..." />
当前位置: 代码迷 >> 综合 >> 【UVa】【二分】【DP】10163 Storage Keepers
  详细解决方案

【UVa】【二分】【DP】10163 Storage Keepers

热度:54   发布时间:2023-11-21 07:06:42.0

UVa 10163 Storage Keepers

题目

◇题目传送门◆(由于UVa太慢,这里提供一个vjudge的链接)
◇题目传送门(vjudge)◆

题目大意

NN个相同的仓库需要一些看守。现有 M 个管理员来应聘,第ii个人的能力值为 p i ,所需工资也是pipi。若将第ii个人派去看守 k 个仓库,则每个仓库的安全系数为?pik??pik?;没有人看守的仓库安全系数为00。求在最小安全系数最大的情况下,最少需要支付的工资总和。

思路

最大化最小值,很明显具有二分的性质。

所以我们用二分求出最小安全系数,再求工资总和。

工资总和最小,这个问题我们可以使用DP解决。

定义 f [ j ] jj个仓库所需的最少工资总和,不难列出状态转移方程:

f [ j ] = min ( f [ j ? k ] + p [ i ] )

其中1iM,1jN,1kj1≤i≤M,1≤j≤N,1≤k≤j

综上,我们在二分的时候使用DP判断并维护最小值。注意:当p[i]/kp[i]/k小于当前最小安全系数的时候退出循环。

正解代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;const int Maxn=100;
const int Maxm=30;
const int INF=0x3f3f3f3f;int N,M,p[Maxm+5];
int f[Maxn+5];
int ans,sum;bool Check(int x) {memset(f,0x3f,sizeof f);f[0]=0;for(int i=1;i<=M;i++)for(int j=N;j>=1;j--)for(int k=1;k<=j;k++) {
   //k为第i个人所需看守的仓库数量if(p[i]/k<x)//当安全系数小于x(当前最小安全系数)时不能转移break;f[j]=min(f[j],f[j-k]+p[i]);}if(f[N]>=INF)return false;sum=f[N];//记录当前最小工资总和return true;
}int main() {#ifdef LOACLfreopen("in.txt","r",stdin);freopen("out.txt","w",stdout);#endifwhile(scanf("%d %d",&N,&M)!=EOF&&N&&M) {sum=ans=0;for(int i=1;i<=M;i++)scanf("%d",&p[i]);int lb=0,ub=1000;while(lb+1<ub) {int mid=lb+(ub-lb)/2;if(Check(mid))ans=mid,lb=mid;else ub=mid;}printf("%d %d\n",ans,sum);}return 0;
}
  相关解决方案