高中OJ3516. 【NOIP2013模拟11.6A组】摧毁巴士站

44 篇文章 0 订阅
3 篇文章 0 订阅

30分:
枚举要摧毁的车站,判断符不符合要求。
100分:
很显然,2^48会超时。

解决办法是有选择性的进行删点。基本的思路就是,一条长度不超过k的最短路径上的点,至少有一个是要被删掉的(至于删除哪个好,可以枚举尝试),删掉一个点后再重新求最短路,如果新求出的最短路径长度仍然不超过k,那么就在新的最短路径上再找出一个点删掉,然后再求最短路径……直到求出的最短路径长度超过k,那么就找到了一个解。但这个解未必是最优的,所以要用递归的方法搜索多组解,以确保能搜到最优解。具体步骤如下。
递归进行下列操作:
1. 寻找起点到终点的最短路径,如果最短路径长度超过k,则表示已找到一种删点方案。记录该方案下的删点数目,回溯再找出下一种方案;否则进入下一步。
2. 枚举最短路径中的除1号点和n号点外的某一个点,将其删除。
3. 回到1递归继续搜索。
这样的搜索顺序使得枚举的点的数量得到控制。搜索的每一层会从L-2个点中选择一个进行删除,L是当前找到的最短路径的长度。如果当前找到的最短路径很长,虽然在这一层我们需要枚举很多个点进行删除,但也说明起点到终点的距离已经很长了,我们离答案已经很接近了。一般来说,在50个点的图中,答案应该不超过20。那么L<20。并且L比较大的情况只会出现在搜索的最后几层。这么想来,这个搜索的时间复杂度是可以接受的。


所以我们可以每次进行一次最短路,每次删掉最短路上的一个点,然后继续回溯。
直到最短路的长度大于k,就记录答案。

代码

var
        a:array[1..4000,1..2] of longint;
        b:array[1..50] of longint;
        c:array[1..51] of longint;
        bz:array[1..50] of boolean;
        d:array[1..10000,1..2] of longint;
        f:array[1..50] of longint;
        bz2:array[1..50,1..50] of boolean;
        xl:array[1..50] of longint;
        n,m,s,i,j,ans,h,t,x,y,tot:longint;
function pd:longint;
var
        i,j,k,l:longint;
begin
        pd:=0;
        h:=0;
        t:=1;
        fillchar(f,sizeof(f),10);
        f[1]:=0;
        d[1,1]:=1;
        d[1,2]:=0;
        while h<t do
        begin
                inc(h);
                j:=d[h,1];
                for i:=c[j] to c[j+1]-1 do
                if (bz[a[i,2]]=false) and (f[j]+1<f[a[i,2]]) then
                begin
                        k:=a[i,2];
                        f[k]:=f[j]+1;
                        inc(t);
                        d[t,1]:=k;
                        d[t,2]:=h;
                        if k=n then
                        begin
                                pd:=f[k];
                                tot:=0;
                                l:=h;
                                while d[l,1]<>1 do
                                begin
                                        inc(tot);
                                        xl[tot]:=d[l,1];
                                        l:=d[l,2];
                                end;
                                exit;
                        end;
                end;
        end;
        pd:=maxlongint;
end;
procedure dg(t:longint);
var
        dl:array[1..50] of longint;
        i,j:longint;
begin
        if t-1>=ans then
        exit;
        if pd>s then
        begin
                if ans>t-1 then
                ans:=t-1;
                exit;
        end;
        dl:=xl;
        j:=tot;
        for i:=1 to j do
        if bz[dl[i]]=false then
        begin
                bz[dl[i]]:=true;
                dg(t+1);
                bz[dl[i]]:=false;
        end;
end;
begin
        assign(Input,'bus.in'); reset(Input);
        assign(Output,'bus.out'); rewrite(Output);
        readln(n,m,s);
        for i:=1 to m do
        begin
                readln(x,y);
                bz2[x,y]:=true;
        end;
        m:=0;
        for i:=1 to n do
                for j:=1 to n do
                if bz2[i,j] then
                begin
                        inc(m);
                        a[m,1]:=i;
                        a[m,2]:=j;
                        inc(b[i]);
                end;
        c[1]:=1;
        for i:=2 to n+1 do
        c[i]:=c[i-1]+b[i-1];
        ans:=maxlongint;
        dg(1);
        writeln(ans);
        close(Input); close(Output);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值