1. 题目
征兵计划(conscription.pas/c/cpp)
题目描述
公元1940年6月22日,德军攻占巴黎,法兰西共和国投降。希特勒为了实现其征服欧洲的野心,准备大举进攻英伦三岛。英国皇家空军紧急启动征兵计划,丘吉尔首相把这项任务交给你负责。
司令部会告诉你每月最少所需新兵的数量。当征召或遣回一个士兵时,会有额外的支出。一旦一个士兵被征召而服役,他将得到政府每月发给的工资。司令部当然会告诉你征召一名士兵的费用,遣回一名士兵的费用和士兵每月工资。现在司令部要求,要把这个征兵计划的费用限制到最低,你每月该征召或遣回多少士兵。
输入数据
输入文件含有三行。第一行为月数n(不超过12)。第二行含征召一个士兵的费用,一个士兵的工资和遣回一个士兵的费用(≤100)。第三行含n个数,分别表示每月最少需要的士兵人数(≤1000)。每个数据之间有一个空格隔开。
输出数据
输出仅一行,表示征兵计划的最小总费用。
样例输入
3
4 5 6
10 9 11
样例输出
199
2. 题目实质
求满足条件的最小代价。(废话)
3. 算法
动态规划。(但是,一共五个点,贪心过三个,搜索过四个,囧)
因为这个题每一个月的状态都受前面的状态的影响,所以考虑动态规划。
建立一个 f 二维数组,f[I,j] 分别表示第 I 个月有 j 个士兵时,前 I 个月总共花的最少钱数,由此可以得出动态转移方程: f[I,j]:=min(f[I-1,k]+ (j-k)*zheng || (k-j)*qian +j*gongzi).
然后,就是这样。
注意 k 与 j 关系不同时的判断。
边界条件是第一个月的人,另外,这里是正着更新。(背包是倒着更新)
4. 注意事项
注意状态转移时的判断。
如何判断一个题是动态规划:当贪心不太好贪,或是可以举出反例,并且搜索复杂度太高时,写方程的话可以从状态的转移下手,感觉状态太多可以加一个变量。实在不行可以多次动态规划,或是正着跑一遍倒着跑一遍。(暴力 DP )
5. 时空复杂度 O(n^3)
6. 程序代码
Leve (pascal)
var
f:array[0..12,0..1000] of longint;
i,j,k,a1,a2,a3,max,ans,n:longint;
a:array[0..12] of longint;
begin
assign(input,'conscription.in');
assign(output,'conscription.out');
reset(input);
rewrite(output);
readln(n);
readln(a1,a2,a3);
for i:=1 to n do
begin
read(a[i]);
if a[i]>max then max:=a[i];
end;
filldword(f,sizeof(f)>>2,maxlongint>>1);
for i:=a[1] to max do
f[1,i]:=i*(a1+a2);
for i:=2 to n do
for j:=a[i] to max do
for k:=a[i-1] to max do
begin
if k>j then
begin
if f[i,j]>f[i-1,k]+a3*(k-j)+j*a2 then
f[i,j]:=f[i-1,k]+a3*(k-j)+j*a2;
end
else
if f[i,j]>f[i-1,k]+a1*(j-k)+j*a2 then
f[i,j]:=f[i-1,k]+a1*(j-k)+j*a2;
end;
ans:=maxlongint;
for i:=a[n] to max do
if f[n,i]<ans then ans:=f[n,i];
writeln(ans);
close(input);
close(output);
end.