题目大意
在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有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.