这个题目是上个礼拜天上午看的,纠结了一上午啊,总算还是有点思路,但是总是感觉到相当的混乱,感觉被伤到了一样。后来想想不必就为这么一个题目纠结,换一个吧。但是不纠结是不可能的。最终,还是没忍住,看了一下人家的解题报告,发现思路大致上还是对的,但是,细节方面处理的很烂,琢磨了半天,几乎都要把人家的代码背下来了。今天早上一起来,就决心把这个伤到我的题目弄掉,但是,因为一个小小的BUG,弄了好一下,我算是明白了,写点代码还是老实点的好啊,免得因为一个BUG要调试半天。。
现在说正题。题目的意思是,老板需要雇人干活,雇佣一个人需要钱h,每个月需要支付他工资s,解雇他还要给他"买断”,需要支付f的钱。现在告诉你月份数m,然后分别给你h,s,f,然后是m个月中每个月需雇佣的最少的人数a[i],求支出最少的结果。
首先,贪心的思路是应该否定的,假设我们按照每个月都恰好雇佣最少需要的人数,那么在雇佣费h和解雇费f很低的情况下,是有可能靠谱的。但是,假设雇佣费或者解雇费很高,而工资很低,那我们宁可让个别人吃空饷,而不去重新雇佣他们或者说事去解雇他们。
那么,这个题目就应该用DP来解决了。假设截止第i个月为止,可能雇佣的最大人数为m[i](m[i]=max{a[1],a[2],…………,a[n]}),这样就可以考虑到为少支出让部分人吃空饷的状况了。令dp[i][j]表示到第i个月时雇佣j人时支出的最小值,那么我们可以得到装填转移方程:dp[i][j]=min{dp[i-1][k]+(j-k)*h+j*s,dp[i-1][r]+(r-j)*f+j*s}其中k<j,m[i]>=r>=j.
接下来,处理好细节就可以了。
下面贴代码:
View Code
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 int main() 5 { 6 int a[13],m[13],*dp[13]; 7 int month,i,j,k,temp,min,max,h,s,f; 8 while(cin>>month) 9 { 10 if(month == 0) 11 break; 12 cin>>h>>s>>f; 13 for(i = 1;i <= month;i++) 14 cin>>a[i]; 15 max = a[1]; 16 for(i = 1;i <= month;i++) 17 { 18 if(a[i] > max) 19 max = a[i]; 20 dp[i] = new int [max + 1]; 21 m[i] = max; 22 } 23 for(i = 1;i <= month;i++) 24 for(j = 0;j <= m[i];j++) 25 dp[i][j] = 0; 26 dp[1][a[1]] = a[1] * (h + s); 27 for(i = 2;i <= month;i++) 28 for(j = a[i];j <= m[i];j++) 29 { 30 min = 0xffffff; 31 for(k = a[i - 1];k <= m[i - 1]&&k < j;k++) 32 if(dp[i - 1][k]) 33 { 34 temp = dp[i - 1][k] + (j - k) * h + j * s; 35 if(temp < min) 36 min = temp; 37 } 38 for(k = j;k <= m[i - 1];k++) 39 if(dp[i - 1][k]) 40 { 41 temp = dp[i - 1][k] + (k - j) * f + j * s; 42 if(temp < min) 43 min = temp; 44 } 45 dp[i][j] = min; 46 }//for j 47 min = 0xffffff; 48 for(i = 1;i <= m[month];i++) 49 if(dp[month][i]) 50 if(dp[month][i] < min) 51 min = dp[month][i]; 52 /*for(i = 1;i <= m[month];i++) 53 cout<<dp[month][i]<<" "; 54 cout<<endl;*/ 55 cout<<min<<endl; 56 for(i = 1;i <= month;i++) 57 delete[] dp[i]; 58 }//while 59 return 0; 60 }