HDU 1158 Employment Planning

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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值