现在的位置: 首页 > 综合 > 正文

HEOI2012 朋友圈

2017年04月23日 ⁄ 综合 ⁄ 共 3000字 ⁄ 字号 评论关闭

转:http://www.cnblogs.com/neverforget/archive/2012/05/10/2493995.html

这是做的最得意的一道题,当时在考场上得了90,是因为数据中A有100++的情况,Runtime_error了。。

首先强调,本人不是用二分图,或者说不用图论算法,只涉及简单的枚举和记忆化思想。

A国的人最多只能取两个,这个大家都能看出来,当然还可能不取。

B国的人不去向常规考虑奇偶分组,这个会在后面提到。

题意抽象为求一个最大完全图,那么当我们确定一个极大完全图后,任意一个元素都可以作为代表元,给定代表元之后,通过一边O(n)的扫描就能得到这个极大完全图。

所以对于不含A国人的情况,我们只需要枚举一个B国人,把其他的B国人尽量多的加进去,这个加人的顺序与结果无关,而代表元可以使任意一个,因此可以用一个数组记录v[x]表示是否求过以x为代表元的集合。

对于有A国人的情况,枚举一个A国人,再枚举一个B国人,同理他们构成的最大完全图也就确定了,同样可以使用记忆化来降低枚举时间。

对于两个A国人的情况,一样处理即可。

程序有两个记录数组v1,v2,由于A,B的人数差距极大,用一个数组记录就需要开10*10*1500,但是事实上的数据会达到200*200*3000,极大的超内存,所以采用分开处理的方式,A较小B较大时用200*200*200的数组,A极小B很大时用50*50*3000的,内存就很充裕了。

而对于时间复杂度,为最大枚举数O(sa*sa*sb*sb),加入的v数组会让它达不到这个极限。

事实证明,时限为20S的题目,我的程序通过最大点的时间仅为2s++

如有反例,欢迎提出。


program friends(input,output);
var
    can:array[0..4050,0..4050] of boolean;
    a:array[0..1005] of longint;
    b:array[0..4010] of longint;
    stack:array[0..600] of longint;
    v1:array[0..310,0..310,0..400] of boolean; 
    v2:array[0..50,0..50,0..3600] of boolean;
    flag:boolean;
    top:longint;
    answer:longint;
    sa,sb,m:longint;
    cases:longint;
function check(x:longint):boolean;
var
    i,sum:longint;
begin
    sum:=0;
    for i:=0 to 30 do
        if (x and (1 shl i))>0 then
            inc(sum);
    if odd(sum) then
        exit(true);
    exit(false);
end;{ check }
procedure init;
var
    i,j,xx,yy:longint;
begin
    readln(sa,sb,m);
    if (sa<=300)and(sb<=350) then
        flag:=true;
    if (sa<=20)and(sb<=3000) then
        flag:=false;
    fillchar(can,sizeof(can),false);
    for i:=1 to sa do
        read(a[i]);
    for i:=1 to sb do
        read(b[i]);
    for i:=1 to m do
    begin
        read(xx,yy);
        can[xx,yy+sa]:=true;
        can[yy+sa,xx]:=true;
    end;
    for i:=1 to sa-1 do
        for j:=i+1 to sa do
            if ((a[i] xor a[j]) mod 2=1) then
            begin
                can[i,j]:=true;
                can[j,i]:=true;
            end;
    for i:=1 to sb-1 do
        for j:=i+1 to sb do
            if ((b[i] xor b[j]) mod 2=0)or(check(b[i] or b[j])) then
            begin
                can[i+sa,j+sa]:=true;
                can[j+sa,i+sa]:=true;
            end;
    fillchar(v1,sizeof(v1),false);
    fillchar(v2,sizeof(v2),false);
    for i:=0 to sa+sb do
    begin
        can[0,i]:=true;
        can[i,0]:=true;
    end;
end;{ init }
procedure calc(xx,yy,zz:longint);
var
    i,j:longint;
    flags:boolean;
begin
    top:=0;
    if xx<>0 then
    begin
        inc(top);
        stack[top]:=xx;
    end;
    if yy<>0 then
    begin    
        inc(top);
        stack[top]:=yy;
    end;
    if zz<>sa then
    begin
        inc(top);
        stack[top]:=zz;
    end;
    if flag then
        v1[xx,yy,zz-sa]:=true
    else
        v2[xx,yy,zz-sa]:=true;
    for i:=1 to sb do
        if (i+sa<>zz) then
        begin
            flags:=true;
            for j:=top downto 1 do
                if not can[i+sa,stack[j]] then
                begin
                    flags:=false;
                    break;
                end;
            if not flags then
                continue;
            inc(top);
            stack[top]:=sa+i;
            if flag then
                v1[xx,yy,i]:=true
            else
                v2[xx,yy,i]:=true
        end;
    if top>answer then
        answer:=top;
end;{ calc }
procedure main;
var
    i,j,k:longint;
begin
    answer:=0;
    if sa=0 then
    begin
        for i:=1 to sb do
            calc(0,0,i);
        exit;
    end;
    if sa=1 then
    begin
        for i:=0 to 1 do
            for j:=sa+1 to sb+sa do
                if (((not v1[0,i,j-sa])and(flag))or((not flag)and(not v2[0,i,j-sa])))and(can[i,j]) then
                    calc(0,i,j);
        exit;
    end;
    for i:=sa+1 to sb+sa do
        calc(0,0,i);
    for i:=0 to sa-1 do
        for j:=i+1 to sa do
            if can[i,j] then
            for k:=sa+1 to sb+sa do
                if (((not v1[i,j,k-sa])and(flag))or((not flag)and(not v2[i,j,k-sa])))and(can[i,k])and(can[j,k]) then
                    calc(i,j,k);
end;{ main }
procedure print;
begin
    writeln(answer);
end;{ print }
begin
    assign(input,'friends.in');reset(input);
    assign(output,'friends.out');rewrite(output);
    readln(cases);
    while cases>0 do
    begin
        dec(cases);
        init;
        main;
        print;
    end;
    close(input);
    close(output);
end.

抱歉!评论已关闭.