P1340 [JZOJ] 周长

\(n\) 块宽度为 \(1\) 的农田,每一块都有自己的长度,小 \(\texttt{PP}\) 可以自己去选择这 \(N\) 块农田的位置,要按如图的方式拼接在一起。每块农田互不相同 (即使长度一样也不同) 那么显然有 \(n!\) 种放置方法。不同的方法可以使整个大农田的周长不同,如图 \(\texttt{(a)}\) 周长为 \(16\),而图 \(\texttt{(b)}\) 的周长为 \(20\)。可证明没有比 \(20\) 更大的周长存在。小 \(\texttt{PP}\) 喜欢绕着农田跑步,他希望最终这个大农田的周长最大。

数据, \(n \leq 15\)

kcf4e0.jpg

这是一个被卡了空间所以 \(50\) 分的假题解。

首先我们发现这道题 \(n \leq 15\),而爆搜是 \(15!\) 绝对会炸。

你会发现 \((\left\lfloor\frac{15}{2}\right\rfloor +1)!\) 是一个我们可以爆搜的数字。然而这就可以考虑一下折半搜索。

我们先定义 \(\texttt{Dfs1}\) 为查找前 \(mid\) 个位置,用 \(n\) 个不同数字的次数,每一种可能用链式前向星存起来,分别要存这种情况的代号,连向这种情况所属左半边的长度还有最右边的长度。对于每一种情况的代号,因为我们每一个数字都要出一次,我们把出场次数当做一个只有 \(0\)\(1\) 的数组 \(used\),它的代号为 \(\sum used_i \times 2^i\),那么 \(\texttt{Dfs2}\) 如果找到了 \(\sum\limits^n 2^i - \sum used_i \times 2^i\) 那么我们就可以认为这两个是可行的配对方案。

这样子我们还会发现状态有点多,而且有些所属左半边的长度还有最右边的长度是一样的,如果把相同的清空且把方案个数相加那么速度会大大提升。但是我们显然不能加入一种状态然后跑到前面去去重。

我们可以把所有状态离线出来后,在外面跑一边 \(O(tot)\) (\(tot\) 为方案个数) 的去重机。这个东西很好想,我们定义一个数组 \(recf[k,p]\) 代表长度为 \(k\) 最后一个数字为 \(p\) 的在链式前向星上的编号。因为 \(a_i \leq 10^2\),这个方案是可行的。每一次用完清空即可。

然后跑一遍 \(\texttt{Dfs2}\) 就完事了。目测这种方法只能跑到 \(12\) 左右,复杂度 \(\frac{tot}{2^n} \times (n-mid)!+mid!\)。但是如果你愿意把这个东西拆成三半也是可以的,目测复杂度是 \((\frac{n}{3}!)^3\),应该是正确的复杂度,跑到 \(15\) 大概没有什么问题,不过我实在懒得写了,还卡空间。正解状压就算了。

// T2

Uses math;

Const
    maxn=100;
    total=12;
    sumway=1500000;
    totnum=total*maxn;

var
    cnt,next,reach,value,times:array[-1..sumway,1..2] of longint;
    recf:array[-1..totnum,-1..maxn] of longint;
    num,get,used:array[-1..total] of longint;
    way:array[-1..sumway] of longint;
    tot:array[1..2] of longint;
    n,i,j,k,op,ans,sum,tmp,mid,time,length:longint;

procedure Add(l,r,sum,kind:longint);
begin
    inc(tot[kind]); next[tot[kind],kind]:=cnt[l,kind]; cnt[l,kind]:=tot[kind];
    reach[tot[kind],kind]:=r; value[tot[kind],kind]:=sum; times[tot[kind],kind]:=1;
end;

procedure Dfs1(x:longint);
var i,tmp:longint;
begin
    if x>mid then
    begin
        length:=get[1]; tmp:=0;
        for i:=1 to n do inc(tmp,used[i]*1 << (i-1));
        for i:=2 to mid do inc(length,abs(get[i]-get[i-1]));
        Add(tmp,length,get[mid],1); exit;
    end;
    for i:=1 to n do
        if used[i]=0 then
        begin
            get[x]:=num[i]; used[i]:=1; Dfs1(x+1); used[i]:=0;
        end;
end;

procedure Dfs2(x:longint);
var i,j:longint;
begin
    if x>n then
    begin
        length:=get[n]; tmp:=0; time:=0;
        for i:=1 to n do inc(tmp,used[i]*1 << (i-1));
        for i:=mid+2 to n do inc(length,abs(get[i]-get[i-1]));
        i:=cnt[op-tmp,2];
        while i<>-1 do
        begin
            sum:=abs(get[mid+1]-value[i,2])+reach[i,2]+length+n << 1;
            inc(way[sum],times[i,2]); ans:=max(ans,sum);
            i:=next[i,2]; inc(time);
        end; exit;
    end;
    for i:=1 to n do
        if used[i]=0 then
        begin
            get[x]:=num[i]; used[i]:=1; Dfs2(x+1); used[i]:=0;
        end;
end;

begin
    filldword(cnt,sizeof(cnt) div 4,maxlongint*2+1);
    read(n); for i:=1 to n do read(num[i]);
    for i:=1 to n do inc(op,1 << (i-1));
    mid:=n >> 1; Dfs1(1);  
    for i:=1 to op do
    begin
        j:=cnt[i,1];
        while j<>-1 do
        begin
            if (recf[reach[j,1],value[j,1]]=0) then
            begin Add(i,reach[j,1],value[j,1],2); recf[reach[j,1],value[j,1]]:=tot[2]; end
            else inc(times[recf[reach[j,1],value[j,1]],2]);
            j:=next[j,1];
        end;
        j:=cnt[i,1];
        while j<>-1 do begin recf[reach[j,1],value[j,1]]:=0; j:=next[j,1]; end;
    end;
    fillchar(used,sizeof(used),0);
    fillchar(get,sizeof(get),0);
    Dfs2(mid+1); writeln(ans,' ',way[ans]);
end.

转载于:https://www.cnblogs.com/FibonacciHeap/articles/10819123.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值