饭票 (bticket.pas)
学校的食堂在使用饭卡之前使用饭票
饭票并不向饭卡一样方便。比如你有1张5元饭票和3张1元饭票,则你无法付4元的饭费。
某天小x去食堂吃饭,手里有n种饭票,面值分别为A1~An,数量分别为C1~Cn
请你计算小x的饭票能组成多少在[1,m]区间内的面值。
Input (bticket.in)
第一行2个数n m,用空格隔开。
以后n个数,分别为A1..An
以后n个数,分别为C1..Cn
Output (bticket.out)
一个数,即问题的答案
Sample
INPUT | OUTPUT |
3 10 1 2 4 2 1 1 | 8 |
约定
1 ≤ n ≤ 100
1 ≤ Ai ≤ m ≤ 100000
1 ≤ Ci ≤ 1000
不看数据范围的话,就会认为纯属水题一道!看了范围后,便会发现还有很有做的价值。
很容易想到的是O(n*m*Cmax)的算法,但是这只有50分!
于是乎,我们必须从循环过程中着手,简化算法。
我是用 f[i+a[i]]:=f[i];(if f[i]=true) 的方法。
在使用背包的时候,很多步骤是重复的......所以很慢.....
看了一牛的题解后http://www.cppblog.com/liaorc/articles/41598.html
突然有了启发:
完全可以像最大升序数列那样做!
记录最少要用多少Ai达到f[j],然后在循环的过程中更新!
这样就可以把循环压缩到O(m*n)!
代码如下:
2 v: array [ 0 .. 110001 ] of boolean;
3 f: array [ 0 .. 110001 ] of longint;
4 begin
5 assign(input, ' bticket.in ' );reset(input);
6 assign(output, ' bticket.out ' );rewrite(output);
7 readln(n,m);
8 for i: = 1 to n do
9 begin
10 read(a[i]);
11 end ;
12 for i: = 1 to n do
13 begin
14 read(b[i]);
15 end ;
16 max: = 0 ;
17 fillchar(v,sizeof(v), 0 );
18 v[ 0 ]: = true;
19 for i: = 1 to n do
20 begin
21 fillchar(f,sizeof(f), 0 );
22 for h: = 0 to m do
23 begin
24 if h + a[i] > m then break;
25 if v[h] then
26 begin
27 if f[h] < b[i] then
28 begin
29 if not (v[h + a[i]]) then
30 begin
31 v[h + a[i]]: = true;
32 f[h + a[i]]: = f[h] + 1 ;
33 end
34 else
35 begin
36 if f[h + a[i]] > f[h] + 1 then
37 begin
38 f[h + a[i]]: = f[h] + 1 ;
39 end ;
40 end ;
41
42 end ;
43 end ;
44 end ;
45 end ;
46 count: = 0 ;
47 for i: = 1 to m do
48 begin
49 if v[i] then inc(count);
50 end ;
51 writeln(count);
52 close(input);close(output);
53 end .
测试结果如下
<?xml version="1.0" ?>