【动态规划】数字游戏(game)
【问题描述】
小W发明了一个游戏,他在黑板上写出了一行数字a1,a2,…an,然后给你m个回合的机会,每回合你可以从中选择一个数擦去它,接着剩下来的每个数字ai都要递减一个值bi如此重复m个回合,所有你擦去的数字之和就是你所得到的分数。
小W和他的好朋友小Y玩了这个游戏,可是他发现,对于每个给出的an和bn序列,小Y的得分总是比他高。小W很不服气,想让你帮他算算,对于每个an和bn序列,可以得到的最大得分是多少。这样他就知道有没有可能超过小Y的得分。
【输入文件】
输入文件game.in的第1行,一个整数n(1≤n≤200),表示数字的个数。
第2行,一个整数m(1≤m≤n),表示回合数。
接下来一行有n个不超过10000的正整数,a1,a2,…an,表示原始数字
最后一行有n个不超过500的正整数,b1,b2,…bn,表示每回合每个数字递减的值
【输出文件】
一个整数,表示最大可能的得分。结果输出到文件game.out。
【样例输入】
3
3
10 20 30
4 5 6
【样例输出】
47
这道题我曾经纠结于所谓“取数”的过程该怎么在方程中表示,也就是说,如何将“取了就不能再取”体现在方程中。后来才醒悟,如果这都能体现出来,那就真成搜索了。。囧rz。
一般这种题,取数都是有顺序上的决策的,如果没有顺序,那就要创造一个顺序。比如这道题。。
题目大意:
有n个数A[1]..A[n],你要从中取走m个数,你每取走一个数,剩下的每个数字都会被减去B[i],求能取走的最大数字和。
如何使结果最优呢?首先,若我们取了A[i]和A[j],且B[i]<B[j],那我们一定是先取A[j]再取A[i],因为A[i]每回合损失较小。
所以,我们取数时的顺序一定是先取递减值大的再取递减值小的,因此我们可以按B[i]数组从大到小排序,则取数的顺序一定是从前往后取。这样,取数的顺序就被找到了,不存在后效性。
在排序以确定取数顺序之后,我们只需考虑要取哪些数。
解题思路:
许多题目都是这样,我们需要先有序化,再开始动规。
f[i,j]表示前i个数中取j个数所能获得的最大数字和
f[i,0]=0
f[i,1]=A[i]
状态转移:
f[i,j]=max{f[i-1,j],f[i-1,j-1]+A[i]-B[i]*(j-1)}
程序代码:
var n,m,i,j,k:integer; a,b:array[1..200] of integer; f:array[0..200,0..200] of longint; procedure qsort(l,r:integer); var i,j,m,t:integer; begin i:=l; j:=r; m:=b[(i+j)div 2]; repeat while b[i]>m do inc(i); while b[j]<m do dec(j); if i<=j then begin t:=a[i]; a[i]:=a[j]; a[j]:=t; t:=b[i]; b[i]:=b[j]; b[j]:=t; inc(i); dec(j); end; until i>j; if l<j then qsort(l,j); if i<r then qsort(i,r); end; begin assign(input,'game.in');reset(input); assign(output,'game.out');rewrite(output); readln(n); readln(m); for i:=1 to n do read(a[i]); readln; for i:=1 to n do read(b[i]); qsort(1,n); for i:=1 to n do f[i,1]:=a[i]; for i:=2 to n do for j:=1 to i do begin if f[i-1,j]>=f[i-1,j-1]+a[i]-b[i]*(j-1) then f[i,j]:=f[i-1,j] else f[i,j]:=f[i-1,j-1]+a[i]-b[i]*(j-1); end; writeln(f[n,m]); close(input);close(output); end.