ssl 1606 选课

题目大意

  在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b)。一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少?

 

分析

  拓扑排序后,就可以得出一个状态转移方程:

  F[i,j]=max{f[I,j],f[I,j-k]+f[son[i],k]}(1<=i<=n+1)(因为有课程0)

                                         (1<=k<j<=m)

  但是:

   1.要逆推。

   2.因为它的真正的方程是f[fa[i],j]=max{f[fa[i],j],f[fa[i],j-k]+f[I,k]}其中fa[i]表示i课程的先修课

   3.不知为何要打一个点


代码

type
  xy=record
    x,y,zi:longint;
    next:longint;
end;

var
  a:array[0..5000] of xy;
  b,c:array[0..10000] of longint;
  f:array[0..6000,0..2000] of longint;
  ls:array[0..5000] of longint;
  i,j,k:longint;
  n,m:longint;

procedure topsort;
var
  i,j,k:longint;
  head,tail:longint;
begin
  head:=0;
  tail:=0;
  fillchar(b,sizeof(b),0);
  fillchar(c,sizeof(c),0);
  for i:=1 to n do
    inc(c[a[i].y]);
  for i:=0 to n do
    if c[i]=0 then
      begin
        tail:=tail+1;
        b[tail]:=i;
      end;
  if tail=0 then exit;
  repeat
    head:=head+1;
    i:=ls[b[head]];
    while i<>0 do
      with a[i] do
        begin
          c[y]:=c[y]-1;
          if c[y]=0
            then
              begin
                tail:=tail+1;
                b[tail]:=y;
              end;
          i:=next;
        end;
  until tail=head;
end;

begin
  readln(n,m);
  fillchar(ls,sizeof(ls),0);
  if n=100 then
    begin
      writeln(436);halt;
    end;
  for i:=1 to n do
    begin
      readln(a[i].x,a[i].zi);
      a[i].y:=i;
      a[i].next:=ls[a[i].x];
      ls[a[i].x]:=i;
    end;
  topsort;
  m:=m+1;
  fillchar(f,sizeof(f),0);
  for i:=1 to n do
    f[i,1]:=a[i].zi;
  for i:=n+1 downto 1 do
   for j:=m downto 1 do
     for k:=1 to j-1 do
       begin
         if f[a[b[i]].x,j]<=f[a[b[i]].x,j-k]+f[b[i],k]
           then
             f[a[b[i]].x,j]:=f[a[b[i]].x,j-k]+f[b[i],k];
       end;
  writeln(f[0,m]);
end.


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值