Bzoj2595: [Wc2008]游览计划

18 篇文章 0 订阅
6 篇文章 0 订阅

原题网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2595

标算是斯坦纳树,其实类似状压dp。
首先答案一定是树形的,否则断开一条环上边,不影响连通性,答案不会变劣。
用f[x][y][s]表示以x,y为根的,经过了至少 s的景点的树,这里的s是压过的0/1表示的景点是否访问过的状态。
有两种状态转移,一种是合并两棵树f[x][y][s]:=f[x][y][t]+f[x][y][s xor t]-cost[x][y](其中t是s的真子集),另一种是换根f[x’][y’]:=f[x][y]+cost[x’][y’]。
其中有一个技巧是枚举真子集时一开始t:=s;每次t:=(t-1) and s;就能得到一个真子集,结束条件是t=0。

由于我是个蒟蒻,所以有两个错调了好久,一个是循环队列head<>tail写成了head< tail;还有一个是在同一层s内的搜索要拿x,y更新tx,ty才符合正常的搜索顺序,我居然写反了。。。

const
  dx:array[1..4] of longint=(0,0,-1,1);
  dy:array[1..4] of longint=(-1,1,0,0);
  INF=maxlongint>>1;
  mx=101;
type
  rec=record
        x,y,s:longint;
      end;
var
  f,cnt:array[1..10,1..10,0..1024] of longint;
  hash:array[0..10,0..10,0..1024] of boolean;
  map:array[1..10,1..10] of boolean;
  pre:array[1..10,1..10,0..1024,0..1] of rec;
  a:array[1..10,1..10] of longint;
  q:array[0..1024] of longint;
  vis:array[0..1024] of boolean;  n,m,e,x,y,k,head,tail,s,t,ans,ansx,ansy,i,line,tot:longint;
procedure SPFA(s:longint);
  var
    vis:array[1..10,1..10] of boolean;
    q:array[0..mx] of rec;
    head,tail,x,y,k,tx,ty:longint;
  begin
    try
    fillchar(vis,sizeof(vis),false);
    head:=1;tail:=1;tot:=0;
    for x:=1 to n do
      for y:=1 to m do
        for k:=1 to 4 do
          begin
            tx:=x+dx[k];
            ty:=y+dy[k];
            if (tx<1)or(ty<1)or(tx>n)or(ty>m)
              then continue;
            if f[x][y][s]+a[tx][ty]<f[tx][ty][s] then
              begin 
                f[tx][ty][s]:=f[x][y][s]+a[tx][ty];
                cnt[tx][ty][s]:=0;
                pre[tx][ty][s][0].x:=x;
                pre[tx][ty][s][0].y:=y;
                pre[tx][ty][s][0].s:=s;
                if not vis[tx][ty] then
                  begin
                    vis[tx][ty]:=true;
                    q[tail].x:=tx;
                    q[tail].y:=ty;
                    inc(tail);if tail>mx then tail:=1;
                  end;
              end;
          end;
    while head<>tail do
      begin
        x:=q[head].x;
        y:=q[head].y;
        inc(head);if head>mx then head:=1;
        for k:=1 to 4 do
          begin
            tx:=x+dx[k];
            ty:=y+dy[k];
            if (tx<1)or(ty<1)or(tx>n)or(ty>m)
              then continue;
            if f[x][y][s]+a[tx][ty]<f[tx][ty][s] then
              begin
                f[tx][ty][s]:=f[x][y][s]+a[tx][ty];
                cnt[tx][ty][s]:=0;
                pre[tx][ty][s][0].x:=x;
                pre[tx][ty][s][0].y:=y;
                pre[tx][ty][s][0].s:=s;
                if not vis[tx][ty] then
                  begin
                    vis[tx][ty]:=true;
                    q[tail].x:=tx;
                    q[tail].y:=ty;
                    inc(tail);if tail>mx then tail:=1;
                  end;
              end;
          end;  
        vis[x][y]:=false;
      end;
  end;
procedure dfs(x,y,s:longint);
  var
    i:longint;
  begin
    if (x=0)or(hash[x][y][s])
      then exit;
    map[x][y]:=true;
    hash[x][y][s]:=true;
    for i:=0 to cnt[x][y][s] do
      dfs(pre[x][y][s][i].x,pre[x][y][s][i].y,pre[x][y][s][i].s);
  end;
begin
  assign(input,'trip.in');reset(input);
  assign(output,'trip.out');rewrite(output);
  read(n,m);e:=-1;
  for x:=1 to n do
    for y:=1 to m do
      for k:=0 to 1<<10-1 do
        f[x][y][k]:=INF;    
  for x:=1 to n do
    for y:=1 to m do
      begin
        read(a[x][y]);
        if a[x][y]=0 then
          begin
            inc(e);
            f[x][y][1<<e]:=a[x][y];
          end;
      end;
  for x:=1 to n do
    for y:=1 to m do
      f[x][y][0]:=a[x][y];
  head:=1;tail:=2;
  q[1]:=0;vis[0]:=true;
  while head<tail do
    begin
      s:=q[head];
      for x:=1 to n do
        for y:=1 to m do
          begin
            t:=s;t:=(t - 1) and s;
            while t>0 do
              begin
                if f[x][y][t]+f[x][y][s xor t]-a[x][y]<f[x][y][s] then
                  begin
                    f[x][y][s]:=f[x][y][t]+f[x][y][s xor t]-a[x][y];
                    cnt[x][y][s]:=1;
                    pre[x][y][s][0].x:=x;
                    pre[x][y][s][0].y:=y;
                    pre[x][y][s][0].s:=t;
                    pre[x][y][s][1].x:=x;
                    pre[x][y][s][1].y:=y;
                    pre[x][y][s][1].s:=s xor t;
                  end;
                t:=(t - 1) and s;
              end;
          end;
      SPFA(s);
      for i:=0 to e do
        if (s and 1 << i=0)and(not vis[s xor 1<<i]) then
          begin
            vis[s xor 1<<i]:=true;
            q[tail]:=s xor 1 << i;
            inc(tail);
          end;
      inc(head);
    end;
  ans:=maxlongint;
  for x:=1 to n do
    for y:=1 to m do
      if f[x][y][1<<(e+1)-1]<ans then
        begin
          ans:=f[x][y][1<<(e+1)-1];
          ansx:=x;
          ansy:=y;
        end;
  if e>=0 then writeln(ans) else writeln(0);
  if e>=0 then dfs(ansx,ansy,1<<(e+1)-1);
  for x:=1 to n do
    begin
      for y:=1 to m do
        if a[x][y]=0
          then write('x')
          else if map[x][y]
                 then write('o')
                 else write('_');
      writeln;
    end;
  close(input);close(output);
end.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值