大概意思:
分别给出N个大学在M个专业上的排名先后次序,要求找出最长的大学序列,使得其中排在前面的大学在任意专业上的排名都要高于排在后面的大学。
输入:第一行一个整数T,表示有多少组数据。
对于每组数据,第一行有两个整数N,M(1<=n,m<=100),分别表示有多少个大学和多少科专业。
然后就是一个M*N的矩阵。如果在第K行,大学I的位置在J。则表示大学I在第K个专业中排名第J位
输出:一共输出T行,每行一个整数。表示最长的大学序列。
简要分析:显而易见,当M=2时,我们可以用NlogN的Dp来做,但是这里的M最大可以达到100,如果把Dp扩展的话,那么时间复杂度为N^M,显然严重超时。
如果大学I比大学J要好,那么我们就从I连一条边到J,权值为1。对于一个最长的序列,我们可以用FLOYD来求最长路的长度,时间复杂度为O(n^3)。考虑到每条边的权值为1,那么我们可以用拓扑排序来扩展。拓展完一个点之后就把与这个点的边删除了,这样也可以求出最长路,其实实质就是在一棵树里面进行搜索,但是从底部往上搜索而已。时间复杂度为O(M*N^2),实际上会比这个数字小很多。
Program Rank_Mpq;
Const Maxn=101;
var
n,m,t:integer;
v,outed:array[0..Maxn] of integer;
g:array[0..Maxn,0..Maxn] of boolean;
rank:array[0..Maxn,0..Maxn] of integer;
procedure init;
var
Flag:boolean;
i,j,k,x:integer;
begin
readln(n,m);
for i:=1 to m do
begin
for j:=1 to n do
begin
read(x);
rank[x,i]:=j;
end;
readln;
end;
fillchar(g,sizeof(g),0);
fillchar(outed,sizeof(outed),0);
for i:=1 to n do
for j:=1 to n do
if i<>j then
begin
Flag:=true;
for k:=1 to m do
if rank[i,k]>rank[j,k] then
begin
Flag:=false;
break;
end;
if Not Flag then continue;
g[i,j]:=true;
inc(outed[i]);
end;
end;
procedure TopSort;
var
Flag:boolean;
i,j,Max,Best:integer;
begin
Best:=0;
fillchar(v,sizeof(v),255);
while true do
begin
Flag:=false;
for i:=1 to n do
if (v[i]<0) and (outed[i]=0) then
begin
Flag:=true;
break;
end;
if Not Flag then break;
max:=0;
for j:=1 to n do
if (g[i,j]) and (v[j]>max) then max:=v[j];
v[i]:=max+1;
if v[i]>Best then Best:=v[i];
for j:=1 to n do
if g[j,i] then dec(outed[j]);
end;
writeln(Best);
end;
begin
assign(input,'Rank.in'); reset(input);
assign(output,'Rank.out'); rewrite(output);
readln(t);
for t:=1 to t do
begin
init;
TopSort;
end;
close(input); close(output);
end.