主题:《快乐的蜜月》
[问题的模型]
在数轴上有一些线段,线段具有权值。一种方案是指一组没有重叠的线段的集合,一种方案的总收益就是这些被选线段的权值和。在所有的方案中,求第k大的收益。
[问题分析]
该题用动态规划解。
先考虑k=1的情况,即每次取最大值。这题线段端点的范围较小(从1到366),适合做阶段。设f(d)表示前d天可以达到的最大收益,Sd表示右端点在第d天的线段集合。<br>
<br>
(1) 如果f(d)不选取[img]http://www.chinaschool.org/aosai/lwjl/images/02-0222-01.gif[/img]中的任意线段,则f(d) = f(d-1)<br>
(2) f(d)只选择[img]http://www.chinaschool.org/aosai/lwjl/images/02-0222-01.gif[/img]中的某条线段(只能选取一条,因为
[img]http://www.chinaschool.org/aosai/lwjl/images/02-0222-01.gif[/img]中线段右端点都相同,选取多条就冲突了)。设选择线段x,其左端点为d',权值为w。显然,当f(d)最大时,前d'天的收益也最大。因此,f(d) = f(d') + w。<br>
于是,得到状态转移方程:<br>
[img]http://www.chinaschool.org/aosai/lwjl/images/02-0222-02.gif[/img]<br>
<br>
其中,x.d'表示线段x的左端点,x.w表示x的权值。<br>
<br>
<br>
<br>
考虑k≠1的情况。<br>
前d天收益第k大的方案中,前j天(j < d)的收益也是前k大的。因此k≠1的情况可以和k=1的情况类似处理。如果要求的最后结果为第k大,只要在每一个阶段都保留前k大的结果。设f(d, n)为前d天可以达到的第n大收益。在计算f(d)的过程中,每次处理x∈[img]http://www.chinaschool.org/aosai/lwjl/images/02-0222-01.gif[/img] 时,可以建立临时数组g,g[i]表示选择了线段x,前d天可以达到的第i大的收益值。于是,g[i] = f(d', i) + x.w (1 ≤ i ≤ k),然后再把g和f(d)进行归并取前k大。<br>
<br>
<br>
<br>
[复杂度]<br>
f的每个元素都是长整型的,因此需要367*100*4 = 146800 bytes的空间。线段集合S最多有20000个元素,每个元素要8 bytes,一共要20000*8 = 160000 bytes。因此本一共大约要300K,是可以承受的。这里,f(d)使用指针数组,而[img]http://www.chinaschool.org/aosai/lwjl/images/02-0222-01.gif[/img]则用链表存储。<br>
<br>
<br>
<br>
按天数划分阶段,实际上处理了每条线段,对一条线段的处理是求临时数组g和归并排序这两个过程,这两项的复杂度都是O(k)级的,所以算法总的复杂度O(r k),可以接受。<br>
<br>
<br>
<br>
[小结]<br>
实际上该题就是把求最大值推广到了求第k大值,按照求最大值的动态规划,可以得出该题的解法。<br>
<br>
<br>
<br>
[参考程序]<br>
<br>
<br>
<br>
{$R-,Q-,S-,T-,V-,W-,X-,Y-}<br>
const<br>
FileIn = 'honey.ina';<br>
FileOut = 'honey.out';<br>
Days : array[0..12]of integer<br>
= (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365);<br>
type<br>
TDay = ^PDay;<br>
PDay = record<br>
d1, d2 : integer;<br>
GType : byte;<br>
Pay : Longint;<br>
next : TDay;<br>
end;<br>
EachDay = array[1..100]of Longint;<br>
var<br>
Request : array[1..366]of TDay;<br>
r : integer;<br>
k, k2, t : byte;<br>
latest : integer;<br>
cost : array[1..100]of longint;<br>
ti, ans : Longint;<br>
d : array[0..366]of ^EachDay;<br>
<br>
<br>
<br>
procedure Initialize;<br>
var day1, day2 : integer;<br>
i, code : integer;<br>
F : text;<br>
s : string;<br>
p : TDay;<br>
<br>
<br>
<br>
procedure process;<br>
var m, d, j : byte;<br>
s1 : string;<br>
begin<br>
j := pos(' ', s); s1 := copy(s, 1, j - 1); delete(s, 1, j + 3);<br>
<br>
<br>
<br>
j := pos('/', s1); val(copy(s1, 1, j - 1), m, code);<br>
val(copy(s1, j + 1, length(s1) - j), d, code);<br>
day1 := days[m - 1] + d;<br>
<br>
<br>
<br>
j := pos(' ', s); s1 := copy(s, 1, j - 1);<br>
delete(s, 1, j);<br>
<br>
<br>
<br>
j := pos('/', s1); val(copy(s1, 1, j - 1), m, code);<br>
val(copy(s1, j + 1, length(s1) - j), d, code);<br>
day2 := days[m - 1] + d - 1;<br>
<br>
<br>
<br>
new(p);<br>
p^.d1 := day1;<br>
p^.d2 := day2;<br>
val(s, p^.Gtype, code);<br>
p^.next := request[day2];<br>
request[day2] := p;<br>
<br>
<br>
<br>
if day2 > latest then latest := day2;<br>
end;<br>
<br>
<br>
<br>
begin<br>
assign(f, filein); reset(f);<br>
readln(f, k, t);<br>
readln(f, i);<br>
if (i mod 400 = 0) or (i mod 4 = 0) and (i mod 100 <> 0) then<br>
for i := 2 to 12 do inc(days[i]);<br>
readln(f, r);<br>
<br>
<br>
<br>
for i := 1 to 366 do request[i] := nil;<br>
<br>
<br>
<br>
for i := 1 to r do<br>
begin<br>
readln(f, s);<br>
process;<br>
end;<br>
for i := 1 to t do readln(f, cost[i]);<br>
close(f);<br>
<br>
<br>
<br>
for i := 1 to latest do<br>
begin<br>
p := request[i];<br>
while p <> nil do<br>
begin<br>
with p^ do pay := cost[gtype] * (d2 - d1 + 1);<br>
p := p^.next;<br>
end;<br>
end;<br>
<br>
<br>
<br>
k2 := k * 2;<br>
end;<br>
<br>
<br>
<br>
procedure Dynamic;<br>
var i, j, l : integer;<br>
po, p1, p2, p3 : integer;<br>
t : longint;<br>
p : TDay;<br>
f : text;<br>
g : array[1..200]of longint;<br>
<br>
<br>
<br>
procedure Sort(l, r : integer);<br>
var<br>
i, j : byte;<br>
x, y : longint;<br>
begin<br>
i := l; j := r; x := g[(l + r) div 2];<br>
repeat<br>
while g[i] > x do i := i + 1;<br>
while x > g[j] do j := j - 1;<br>
if i <= j then<br>
begin<br>
y := g[i]; g[i] := g[j]; g[j] := y;<br>
i := i + 1; j := j - 1;<br>
end;<br>
until i > j;<br>
if l < j then Sort(l, j);<br>
if i < r then Sort(i, r);<br>
end;<br>
<br>
<br>
<br>
begin<br>
for i := 0 to latest do new(d[i]);<br>
d[0]^[1] := 0;<br>
for i := 2 to k do d[0]^[i] := -1;<br>
<br>
<br>
<br>
for j := 1 to latest do<br>
begin<br>
p := Request[j];<br>
d[j]^ := d[j - 1]^;<br>
while p <> nil do<br>
begin<br>
with p^ do<br>
begin<br>
for i := 1 to k do<br>
begin<br>
if d[d1 - 1]^[i] <> -1 then<br>
g[i] := d[d1 - 1]^[i] + pay<br>
else<br>
g[i] := -1;<br>
g[i + k] := d[j]^[i];<br>
end;<br>
sort(1, k2);<br>
p1 := 1;<br>
p3 := 1;<br>
d[j]^[p3] := -1;<br>
while p1 < 2 * k do<br>
begin<br>
if g[p1] = g[p1 + 1] then inc(p1)<br>
else<br>
begin<br>
d[j]^[p3] := g[p1];<br>
inc(p3);<br>
if p3 > k then break;<br>
inc(p1);<br>
end;<br>
end;<br>
while p3 < k do<br>
begin<br>
inc(p3);<br>
d[j]^[p3] := -1;<br>
end;<br>
end;<br>
p := p^.next;<br>
end;<br>
end;<br>
assign(f, fileout); rewrite(f);<br>
writeln(f, d[latest]^[k]);<br>
close(f);<br>
end;<br>
<br>
<br>
<br>
<br>
begin<br>
ti := meml[$40:$6c];<br>
<br>
<br>
<br>
Initialize;<br>
Dynamic;<br>
<br>
<br>
<br>
writeln((meml[$40:$6c] - ti) / 18.2 : 0 : 2)<br>
end.<br>
<br>
在数轴上有一些线段,线段具有权值。一种方案是指一组没有重叠的线段的集合,一种方案的总收益就是这些被选线段的权值和。在所有的方案中,求第k大的收益。
[问题分析]
该题用动态规划解。
先考虑k=1的情况,即每次取最大值。这题线段端点的范围较小(从1到366),适合做阶段。设f(d)表示前d天可以达到的最大收益,Sd表示右端点在第d天的线段集合。<br>
<br>
(1) 如果f(d)不选取[img]http://www.chinaschool.org/aosai/lwjl/images/02-0222-01.gif[/img]中的任意线段,则f(d) = f(d-1)<br>
(2) f(d)只选择[img]http://www.chinaschool.org/aosai/lwjl/images/02-0222-01.gif[/img]中的某条线段(只能选取一条,因为
[img]http://www.chinaschool.org/aosai/lwjl/images/02-0222-01.gif[/img]中线段右端点都相同,选取多条就冲突了)。设选择线段x,其左端点为d',权值为w。显然,当f(d)最大时,前d'天的收益也最大。因此,f(d) = f(d') + w。<br>
于是,得到状态转移方程:<br>
[img]http://www.chinaschool.org/aosai/lwjl/images/02-0222-02.gif[/img]<br>
<br>
其中,x.d'表示线段x的左端点,x.w表示x的权值。<br>
<br>
<br>
<br>
考虑k≠1的情况。<br>
前d天收益第k大的方案中,前j天(j < d)的收益也是前k大的。因此k≠1的情况可以和k=1的情况类似处理。如果要求的最后结果为第k大,只要在每一个阶段都保留前k大的结果。设f(d, n)为前d天可以达到的第n大收益。在计算f(d)的过程中,每次处理x∈[img]http://www.chinaschool.org/aosai/lwjl/images/02-0222-01.gif[/img] 时,可以建立临时数组g,g[i]表示选择了线段x,前d天可以达到的第i大的收益值。于是,g[i] = f(d', i) + x.w (1 ≤ i ≤ k),然后再把g和f(d)进行归并取前k大。<br>
<br>
<br>
<br>
[复杂度]<br>
f的每个元素都是长整型的,因此需要367*100*4 = 146800 bytes的空间。线段集合S最多有20000个元素,每个元素要8 bytes,一共要20000*8 = 160000 bytes。因此本一共大约要300K,是可以承受的。这里,f(d)使用指针数组,而[img]http://www.chinaschool.org/aosai/lwjl/images/02-0222-01.gif[/img]则用链表存储。<br>
<br>
<br>
<br>
按天数划分阶段,实际上处理了每条线段,对一条线段的处理是求临时数组g和归并排序这两个过程,这两项的复杂度都是O(k)级的,所以算法总的复杂度O(r k),可以接受。<br>
<br>
<br>
<br>
[小结]<br>
实际上该题就是把求最大值推广到了求第k大值,按照求最大值的动态规划,可以得出该题的解法。<br>
<br>
<br>
<br>
[参考程序]<br>
<br>
<br>
<br>
{$R-,Q-,S-,T-,V-,W-,X-,Y-}<br>
const<br>
FileIn = 'honey.ina';<br>
FileOut = 'honey.out';<br>
Days : array[0..12]of integer<br>
= (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365);<br>
type<br>
TDay = ^PDay;<br>
PDay = record<br>
d1, d2 : integer;<br>
GType : byte;<br>
Pay : Longint;<br>
next : TDay;<br>
end;<br>
EachDay = array[1..100]of Longint;<br>
var<br>
Request : array[1..366]of TDay;<br>
r : integer;<br>
k, k2, t : byte;<br>
latest : integer;<br>
cost : array[1..100]of longint;<br>
ti, ans : Longint;<br>
d : array[0..366]of ^EachDay;<br>
<br>
<br>
<br>
procedure Initialize;<br>
var day1, day2 : integer;<br>
i, code : integer;<br>
F : text;<br>
s : string;<br>
p : TDay;<br>
<br>
<br>
<br>
procedure process;<br>
var m, d, j : byte;<br>
s1 : string;<br>
begin<br>
j := pos(' ', s); s1 := copy(s, 1, j - 1); delete(s, 1, j + 3);<br>
<br>
<br>
<br>
j := pos('/', s1); val(copy(s1, 1, j - 1), m, code);<br>
val(copy(s1, j + 1, length(s1) - j), d, code);<br>
day1 := days[m - 1] + d;<br>
<br>
<br>
<br>
j := pos(' ', s); s1 := copy(s, 1, j - 1);<br>
delete(s, 1, j);<br>
<br>
<br>
<br>
j := pos('/', s1); val(copy(s1, 1, j - 1), m, code);<br>
val(copy(s1, j + 1, length(s1) - j), d, code);<br>
day2 := days[m - 1] + d - 1;<br>
<br>
<br>
<br>
new(p);<br>
p^.d1 := day1;<br>
p^.d2 := day2;<br>
val(s, p^.Gtype, code);<br>
p^.next := request[day2];<br>
request[day2] := p;<br>
<br>
<br>
<br>
if day2 > latest then latest := day2;<br>
end;<br>
<br>
<br>
<br>
begin<br>
assign(f, filein); reset(f);<br>
readln(f, k, t);<br>
readln(f, i);<br>
if (i mod 400 = 0) or (i mod 4 = 0) and (i mod 100 <> 0) then<br>
for i := 2 to 12 do inc(days[i]);<br>
readln(f, r);<br>
<br>
<br>
<br>
for i := 1 to 366 do request[i] := nil;<br>
<br>
<br>
<br>
for i := 1 to r do<br>
begin<br>
readln(f, s);<br>
process;<br>
end;<br>
for i := 1 to t do readln(f, cost[i]);<br>
close(f);<br>
<br>
<br>
<br>
for i := 1 to latest do<br>
begin<br>
p := request[i];<br>
while p <> nil do<br>
begin<br>
with p^ do pay := cost[gtype] * (d2 - d1 + 1);<br>
p := p^.next;<br>
end;<br>
end;<br>
<br>
<br>
<br>
k2 := k * 2;<br>
end;<br>
<br>
<br>
<br>
procedure Dynamic;<br>
var i, j, l : integer;<br>
po, p1, p2, p3 : integer;<br>
t : longint;<br>
p : TDay;<br>
f : text;<br>
g : array[1..200]of longint;<br>
<br>
<br>
<br>
procedure Sort(l, r : integer);<br>
var<br>
i, j : byte;<br>
x, y : longint;<br>
begin<br>
i := l; j := r; x := g[(l + r) div 2];<br>
repeat<br>
while g[i] > x do i := i + 1;<br>
while x > g[j] do j := j - 1;<br>
if i <= j then<br>
begin<br>
y := g[i]; g[i] := g[j]; g[j] := y;<br>
i := i + 1; j := j - 1;<br>
end;<br>
until i > j;<br>
if l < j then Sort(l, j);<br>
if i < r then Sort(i, r);<br>
end;<br>
<br>
<br>
<br>
begin<br>
for i := 0 to latest do new(d[i]);<br>
d[0]^[1] := 0;<br>
for i := 2 to k do d[0]^[i] := -1;<br>
<br>
<br>
<br>
for j := 1 to latest do<br>
begin<br>
p := Request[j];<br>
d[j]^ := d[j - 1]^;<br>
while p <> nil do<br>
begin<br>
with p^ do<br>
begin<br>
for i := 1 to k do<br>
begin<br>
if d[d1 - 1]^[i] <> -1 then<br>
g[i] := d[d1 - 1]^[i] + pay<br>
else<br>
g[i] := -1;<br>
g[i + k] := d[j]^[i];<br>
end;<br>
sort(1, k2);<br>
p1 := 1;<br>
p3 := 1;<br>
d[j]^[p3] := -1;<br>
while p1 < 2 * k do<br>
begin<br>
if g[p1] = g[p1 + 1] then inc(p1)<br>
else<br>
begin<br>
d[j]^[p3] := g[p1];<br>
inc(p3);<br>
if p3 > k then break;<br>
inc(p1);<br>
end;<br>
end;<br>
while p3 < k do<br>
begin<br>
inc(p3);<br>
d[j]^[p3] := -1;<br>
end;<br>
end;<br>
p := p^.next;<br>
end;<br>
end;<br>
assign(f, fileout); rewrite(f);<br>
writeln(f, d[latest]^[k]);<br>
close(f);<br>
end;<br>
<br>
<br>
<br>
<br>
begin<br>
ti := meml[$40:$6c];<br>
<br>
<br>
<br>
Initialize;<br>
Dynamic;<br>
<br>
<br>
<br>
writeln((meml[$40:$6c] - ti) / 18.2 : 0 : 2)<br>
end.<br>
<br>