回 帖 发 新 帖 刷新版面

主题:《快乐的蜜月》

[问题的模型]
  在数轴上有一些线段,线段具有权值。一种方案是指一组没有重叠的线段的集合,一种方案的总收益就是这些被选线段的权值和。在所有的方案中,求第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>

回复列表 (共3个回复)

沙发

我老是觉得你没把问题说清楚.看了好几个都这样.我实干着急.
说清楚些好吗.过几天我要期末考试了也没多少时间来看了.拜托了.

板凳

给人一看,好像在叫人从不同的组合中求第k大的.
我想若是这样的话你也不会花这样大的篇幅来写.
清楚些好吗?

3 楼

蜜月过得好不爽   傻眼了  呵!

我来回复

您尚未登录,请登录后再回复。点此登录或注册