UVA 10163-Storage Keepers(DP)

题目大意:有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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值