This filename has no special meanings (tfnhnsm)

本文介绍了一种解决特种部队探索隐蔽军用基地问题的算法。该算法涉及分数规划与最大流问题,需找到从基地出发至各出入口的最优路径,以最小化行动危险性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

This filename has no special meanings
(tfnhnsm)
【问题描述】
现在,保密成为一个很重要也很困难的问题。如果没有做好,后果是严重的。
比如,有个人没有自己去修电脑,又没有拆硬盘,后来的事大家都知道了。
当然,对保密最需求的当然是军方,其次才是像那个人。为了应付现在天上
飞来飞去的卫星,军事基地一般都会建造在地下。
某K 国的军事基地是这样子的:地面上两排大天井共n1 个作为出入口,内
部是许多除可以共享出入口外互不连通的空腔,每个空腔有且只有两个出入口,
并且这两个出入口不会在同一排。为了方便起见,两排出入口分别编号为1,3,5…
和2,4,6…并且最大的编号为n1。
虽然上面扯了那么多关于保密的东西,但是其实解密也是一件很纠结的事

情。但其实最简单直接暴力无脑的解密方法就是找个人去看看。。。

我们有很牛X 的特种部队,只需要派出一支特种部队到K 国基地的某个出
入口,那么和这个出入口直接相连的所有空腔都可以被探索,但也只有这些空腔
可以被这支部队探索。现在有足够多的特种部队可以供你调遣,你必须使用他们

调查完所有的K 国基地内的空腔。
当然,你的基地离K 国基地不会太近,周边的地图将会给你,表示为n 个检
查点和m 条连接这些点的道路,其中点1 到点n1 就是K 国基地的出入口,点n
是你的部队的出发点。对每条道路,有不同的通行时间t 和安全系数s。因为情
报部门只对单向的道路安全系数进行了评估,所以这些道路只允许单向通行,并
且不会存在环。
一支特种部队从你的基地出发,通过某条路径,到达某个K 国基地出入口,
此时这支部队的危险性表示为总时间和这条路径经过的所有道路的安全系数和
的比值。整个行动的危险性表示为你派出的所有部队的危险性之和。你需要使这
个值最小的情况下探索整个K 国基地。

快点完成这个任务,在K 国的叫兽宣布你是K 国人之前。
【输入格式】
第一行2 个正整数n,m (4 <= n <= 700, m <= 100000) 表示整个地区地图上
的检查点和道路数。
下面m 行,每行4 个正整数a, b, t, s(a, b <=n, 1 <= t, s <= 10)表示一条从a 到
b 的道路需时为t,安全系数为s。
接下来1 行2 个正整数m1 和n1(m1 <= 40000, n1 < min{n, 161}), m1 表示K
国基地空腔的个数,n1 表示K 国基地出入口的个数。
再接下来m1 行,每行2 个正整数u, v (u, v<=n1, u 是奇数,v 是偶数),表示
每个空腔的2 个出入口。
【输出格式】
一行,最小的危险性,保留一位小数。或者输出”-1”(无引号)表示此任务
不可能完成。
【样例输入】

5 5
5 1 10 1
5 1 10 1
5 2 9 1
5 3 7 1
5 4 8 1
4 4
1 2
1 4
3 2
3 4
【样例输出】

17.0
【数据规模和约定】
对30%的数据,n<=30
对60%的数据,n<=300

另外40%的数据n1<=20


首先用分数规划求1..n1到达所需最小费用

然后就成为了一个求最小代价全覆盖的问题

问题就转换为了最大流问题


program tfnhnsm;
const
  eps=1e-5;
var
  u,v,tot,n,m,i,j,k,a,b,t,p,m1,n1:longint;
  dl:array [0..50001] of longint;
  dis,cost:array [0..701] of double;
  root:array [0..701] of longint;
  point,safe,time,next:array [0..100001] of longint;
  all,max,ans,s,e,mid:double;
  last:array [0..162] of longint;
  yes:array [0..162] of boolean;
  can:array [0..701] of boolean;
  flow:array [0..162,0..162] of double;
  dd:array [0..162,0..1001] of longint;

function spfa (ans:double;now:longint):boolean;
var
  i,k,tot:longint;
begin
  for i:=1 to n do
    dis[i]:=1e+20;
  i:=1;
  tot:=1;
  dl[1]:=n;
  dis[n]:=0;
  while i<=tot do
    begin
      k:=root[dl[i]];
      while k<>0 do
        begin
          if (dis[point[k]]>dis[dl[i]]+safe[k]-time[k]*ans) then
            begin
              dis[point[k]]:=dis[dl[i]]+safe[k]-time[k]*ans;
              if (point[k]=now)and(dis[now]<eps) then exit(true);
              inc(tot);
              dl[tot]:=point[k];
            end;
          k:=next[k];
        end;
      inc(i);
    end;
  exit(dis[now]<eps);
end;

procedure search (now:longint);
begin
  s:=0;
  e:=24;
  while e-s>eps do
    begin
      mid:=(s+e)/2;
      if spfa(mid,now) then e:=mid
                       else s:=mid;
    end;
  cost[now]:=s;
end;

procedure connect (a,b,s,t:longint);
begin
  inc(tot);
  point[tot]:=b;
  safe[tot]:=s;
  time[tot]:=t;
  next[tot]:=root[a];
  root[a]:=tot;
end;

procedure maxflow;
begin
  repeat
    fillchar(yes,sizeof(yes),false);
    tot:=1;
    i:=1;
    yes[0]:=true;
    dl[1]:=0;
    while i<=tot do
      begin
        if dl[i]=n1+1 then break;
        for j:=1 to dd[dl[i],0] do
          if (flow[dl[i],dd[dl[i],j]]>0)and(not yes[dd[dl[i],j]]) then
            begin
              yes[dd[dl[i],j]]:=true;
              inc(tot);
              dl[tot]:=dd[dl[i],j];
              last[dl[tot]]:=dl[i];
            end;
        inc(i);
      end;
    if not yes[n1+1] then break;
    i:=n1+1;
    max:=9999999999;
    while i<>0 do
      begin
        if flow[last[i],i]<max then max:=flow[last[i],i];
        i:=last[i];
      end;
    ans:=ans+max;
    i:=n1+1;
    while i<>0 do
      begin
        flow[last[i],i]:=flow[last[i],i]-max;
        flow[i,last[i]]:=flow[i,last[i]]+max;
        i:=last[i];
      end;
  until false;
end;

procedure floodfill;
begin
  i:=1;
  tot:=1;
  dl[1]:=n;
  can[n]:=true;
  while i<=tot do
    begin
      k:=root[dl[i]];
      while k<>0 do
        begin
          if not can[point[k]] then
            begin
              can[point[k]]:=true;
              inc(tot);
              dl[tot]:=point[k];
            end;
          k:=next[k];
        end;
      inc(i);
    end;
end;

begin
  assign(input,'tfnhnsm.in');
  reset(input);
  assign(output,'tfnhnsm.out');
  rewrite(output);
  read(n,m);
  for i:=1 to m do
    begin
      read(a,b,t,p);
      connect(a,b,t,p);
    end;
  read(m1,n1);
  fillchar(can,sizeof(can),false);
  floodfill;
  all:=0;
  for i:=1 to n1 do
    if can[i] then
      begin
        search(i);
        all:=all+cost[i];
      end
              else
    cost[i]:=9999999;
  for i:=1 to m1 do
    begin
      read(u,v);
      if (not can[u])and(not can[v]) then
        begin
          writeln(-1);
          close(input);
          close(output);
          halt;
        end;
      dd[u,0]:=dd[u,0]+1;
      dd[u,dd[u,0]]:=v;
      dd[v,0]:=dd[v,0]+1;
      dd[v,dd[v,0]]:=u;
      flow[u,v]:=999999999;
    end;
  for i:=1 to n1 do
    if i and 1 = 1 then
      begin
        dd[0,0]:=dd[0,0]+1;
        dd[0,dd[0,0]]:=i;
        dd[i,0]:=dd[i,0]+1;
        dd[i,dd[i,0]]:=0;
        flow[0,i]:=cost[i];
      end
                   else
      begin
        dd[n1+1,0]:=dd[n1+1,0]+1;
        dd[n1+1,dd[n1+1,0]]:=i;
        dd[i,0]:=dd[i,0]+1;
        dd[i,dd[i,0]]:=n1+1;
        flow[i,n1+1]:=cost[i];
      end;
  ans:=0;
  maxflow;
  writeln(ans:0:1);
  close(input);
  close(output);
end.




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值