这题有两种解题思路
1.dfs 但是由于dfs可能很多状态会重复搜索,所以需要记忆化搜索。
记忆化搜索的典型方式是: dfs +dp
另dp[i][sum] 表示 第i个月时有sum个人时,剩余月份所需开销最小。 则dp[0][0] 即为最后结果。
对于第i 个月 刚开始有sum 个人,sum如果小于等于,则需要新招聘人员,那么dp[i][sum]=dp[i+1][person[i]] +cost[i]
如果sum大于当月需要人数,则需要减员,这时需要遍历减几个人是剩余月份开销最小。
记忆化搜索代码:
int dfs(int index,int sum){
//the condition need return
if(index==n) return 0;
//remember search dfs+dp
if(dp[index][sum]!=-1) return dp[index][sum];
int ans=0;
// need hire
if(sum<=month[index]){
ans=dfs(index+1,month[index])+(month[index]-sum)*h+month[index]*s;
}else{
//need fire some
ans=INF;
for(int j=month[index];j<=sum;j++){
ans=min(ans,dfs(index+1,j)+(sum-j)*f+j*s);
}
}
dp[index][sum]=ans;
return dp[index][sum];
}
- 第二种思路是dp,其实记忆化dfs 也相当于一种dp 吧 ,主要是由于记忆了结果,减少了dfs次数,否则可能超时。
就是完全用dp的思想做
dp[i][j]=min{ dp[i-1][k] +cost[i][k],dp[i][j]} ; k取值范围是i-1 个月需要的人数,到最多需要多少人。 month[i-1]<=k<=personMax . 而对于cost[i][k] 表示第i个月刚开始有k个人时的需要的本月开销。
最后结果是 dp[n-1][j] month[n-1]<=j<=personMax
personMax 是各个月份需要的人的最大值。
代码
int DP(){
// Init
for(int i=month[0];i<=personMax;i++){
dp[0][i]=i*(h+s);
}
for(int i=1;i<n;i++){
for(int j=month[i];j<=personMax;j++){
int mm=INF;
for(int k=month[i-1];k<=personMax;k++){
int cost=0;
if(k<=j){
cost=(j-k)*h+j*s;
}else{
// need fire
cost=(k-j)*f+j*s;
}
mm=min(mm,dp[i-1][k]+cost);
}
dp[i][j]=mm;
}
}
int ans=INF;
for(int i=month[n-1];i<=personMax;i++){
if(dp[n-1][i]<ans){
ans=dp[n-1][i];
}
}
return ans;
}
AC 代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=15;
const int INF=((1<<31)-1)/2;
int n;
int h,s,f; // hire ,salary ,fire;
int month[maxn];
int dp[maxn][200];
int total=0;
int personMax=0;
int dfs(int index,int sum){
if(index==n) return 0;
//remember search dfs+dp
if(dp[index][sum]!=-1) return dp[index][sum];
int ans=0;
// need hire
if(sum<=month[index]){
ans=dfs(index+1,month[index])+(month[index]-sum)*h+month[index]*s;
}else{
//need fire some
ans=INF;
for(int j=month[index];j<=sum;j++){
ans=min(ans,dfs(index+1,j)+(sum-j)*f+j*s);
}
}
dp[index][sum]=ans;
return dp[index][sum];
}
int DP(){
// Init
for(int i=month[0];i<=personMax;i++){
dp[0][i]=i*(h+s);
}
for(int i=1;i<n;i++){
for(int j=month[i];j<=personMax;j++){
int mm=INF;
for(int k=month[i-1];k<=personMax;k++){
int cost=0;
if(k<=j){
cost=(j-k)*h+j*s;
}else{
// need fire
cost=(k-j)*f+j*s;
}
mm=min(mm,dp[i-1][k]+cost);
}
dp[i][j]=mm;
}
}
int ans=INF;
for(int i=month[n-1];i<=personMax;i++){
if(dp[n-1][i]<ans){
ans=dp[n-1][i];
}
}
return ans;
}
int main()
{
while(scanf("%d",&n)!=EOF&&n!=0){
memset(dp,-1,sizeof(dp));
scanf("%d%d%d",&h,&s,&f);
for(int i=0;i<n;i++){
scanf("%d",&month[i]);
if(month[i]>personMax){
personMax=month[i];
}
}
int ans=0;
// ans=dfs(0,0);
ans=DP();
printf("%d\n",ans);
}
return 0;
}