http://acm.hdu.edu.cn/showproblem.php?pid=1158
第一个完全搞懂的动规题!!前面的都是看代码,搞懂动规的思路和解题方式。值得纪念!!
问题:
工厂招工人,招一人花费a元,每个月每个工人的工资为b元,解雇一个工人要支付他c元。
给你n个月工作计划,每个月最少需要的人数为存在m[i]中。求n个月最少花费多少元。
分析:
1、我们可以知道,工厂里面的人数应该总体保持在工作计划中最少人数(numin)和最多人数(numax)之间,然而每个月工人最少要m[i]个。所以我们可以用dp[i][j]存储他再第i个月招j个人最少的花费。
2、这个是一个完全背包问题,即在第i个月的时候要吧用的人从第m[i]个一直求到numax个人最少的花费存下来。状态转移方程则要分情况了,在代码中体现。
3、以上两点不懂的用题目中得例子来说明,如下图:
如图,在二月份的时候,招9、10、11个人时从1月的10个人和11转移下来花费不一样,所以取其中的最小值,所以有一个min。
如果还是不懂,则把下面代码里面的所有注释删掉,然后看一下有min dp[i][k]和没有min dp[i][k]后就可以明白了。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int dp[13][99999];
int main()
{
int n,i,j,k,a,b,c,numax,numin;
int m[13],sum;
while(scanf("%d",&n)&&n)
{
numax = 0;
numin = 999999;
memset(dp,999999,sizeof(dp));
scanf("%d%d%d",&a,&b,&c);
for(i = 1; i <= n; i++)
{
scanf("%d",&m[i]);
if(m[i] > numax)
{
numax = m[i]; //保存招的最多人数
}
if(m[i] < numin)
{
numin = m[i]; //保存招的最少人数
}
}
for(i = m[1]; i <= numax; i++)
{
dp[1][i] = i*a + i*b;
// printf("%d ",dp[1][i]);
}
// printf("\n");
for(i = 2; i <= n; i++)//第二个月到第n个月
{
for(j = m[i-1]; j <= numax; j++)
{
for(k = m[i]; k <= numax; k++)
{
if(k >= j)
{
dp[i][k] = min(dp[i][k],dp[i-1][j]/*上个月用j个人的花费*/ + b*k/*这个月用k个工人的工资*/ + a*(k-j)/*招人的花费*/); //这个月用k个人的花费
}
else
{
dp[i][k] = min(dp[i][k],dp[i-1][j] + b*k + c*(j-k)/*解雇的花费*/);
}
// printf("i:%d j:%d k:%d %d ",i,j,k,dp[i][k]); //如果不知道上面min的作用,把min和dp[i][k]还有所有注释删掉在用这个输出则就能明白了
}
// printf("\n");
}
}
sum = 9999999;
for(i = m[n]; i <= numax; i++)
{
if(dp[n][i] < sum)
{
sum = dp[n][i];
}
}
printf("%d\n",sum);
}
return 0;
}