bzoj 3205: [Apio2013]机器人 (斯坦纳树)

题目描述

传送门

题解

这道题改spfa的姿势+卡常+记忆化,搞了一下午。。。。
f[i][j][x][y] 表示把i-j机器人合并到点 (x,y) 的最小代价。
考虑怎么转移?
(1) f[i][j][x][y]=min{f[i][k][x][y]+f[k+1][j][x][y]}
(2) f[i][j][x][y]=min{f[i][j][x][y]+1}
对于每一个点还需要预处理 dis[x][y][k] ,表示将 (x,y) 处的机器人向 k <script type="math/tex" id="MathJax-Element-1312">k</script>方向推可以到达的位置,这个可以用记忆化+dfs的方式做。
这个转移的方式实际上和斯坦纳树的转移方式基本相同,转移(2)可以用spfa进行松弛。
这道题时限比较卡,所以我们需要改进spfa的方式。
因为所有的边权都是1,所以如果是单源最短路的话我们改成bfs的方式,但是这个是多源的。
我们先将所有可行的位置加入队列,然后对于序列中的点按照f值进行排序,使其从小到大依次存入q1队列中。(计数排序比较快)
然后对于spfa扩展到的点加入q2队列,每次从两个队列的队首选取较小的弹出来进行更新。

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#include<ctime>
#define N 503
#define M 600003
using namespace std;
int dx[5]={0,1,0,-1},dy[5]={1,0,-1,0};
struct data{
    int x,y; 
    data(int X=0,int Y=0) {
        x=X,y=Y;
    }
}dis[N][N][5],q[N*N],h1[M+5];
int f[10][10][N][N],n,w,h,INF,vis[N][N][5],ti,tp,sum[M+5],val[N*N];
bool can[N][N]; char mp[N][N];
data dfs(int x,int y,int k)
{
    if (vis[x][y][k]==ti) return data(-1,0);
    vis[x][y][k]=ti;
    if (dis[x][y][k].x!=0) return dis[x][y][k];
    int t=k;
    if (mp[x][y]=='C') k=(k+1)%4;
    if (mp[x][y]=='A') k=(k+3)%4;
    int x1=x+dx[k]; int y1=y+dy[k];
    if (x1>w||x1<1||y1>h||y1<1||mp[x1][y1]=='x') return dis[x][y][t]=data(x,y);
    return dis[x][y][t]=dfs(x1,y1,k);
}
void spfa(int l,int r)
{
    memset(sum,0,sizeof(sum));  
    memset(can,0,sizeof(can)); 
    int mn=INF,mx=-INF;
    for (int i=1; i<=tp; i++){  
        sum[val[i]]++;  
        mn=min(mn,val[i]); mx=max(mx,val[i]);  
        can[q[i].x][q[i].y]=1;  
    }  
    for (int i=mn+1;i<=mx;i++) sum[i]+=sum[i-1];  
    for (int i=1;i<=tp;i++) h1[sum[val[i]]--]=q[i];  
    for (int i=1;i<=tp;i++) q[i]=h1[tp-i+1];  
    int head=0,tail=0,now; data u,v;  
    while (tp||head!=tail){  
        now=head%M+1;  
        if (head==tail||tp&&f[l][r][q[tp].x][q[tp].y]<f[l][r][h1[now].x][h1[now].y])  
            u=q[tp--]; else{ head=now; u=h1[head]; }  
        int x=u.x; int y=u.y;
        can[x][y]=0;  
        for (int i=0;i<4;i++){  
            int tx=dis[x][y][i].x;
            int ty=dis[x][y][i].y;  
            if (tx!=-1&&f[l][r][x][y]+1<f[l][r][tx][ty]){  
                f[l][r][tx][ty]=f[l][r][x][y]+1;  
                if (!can[tx][ty]){  
                    can[tx][ty]=1; tail=tail%M+1; h1[tail]=data(tx,ty);  
                }  
            }  
        }  
    }  
}
int main()
{
    scanf("%d%d%d",&n,&h,&w);
    for (int i=1;i<=w;i++) scanf("%s",mp[i]+1);
    memset(f,127/3,sizeof(f));
    INF=f[0][0][0][0];  
    for (int i=1;i<=w;i++)
     for (int j=1;j<=h;j++)
      if (mp[i][j]!='x') 
       for (int k=0;k<4;k++)
        ++ti,dis[i][j][k]=dfs(i,j,k);
    for (int i=1;i<=w;i++)
     for (int j=1;j<=h;j++)
      if (mp[i][j]>='0'&&mp[i][j]<='9') {
        int t=mp[i][j]-'0';
        f[t][t][i][j]=0; 
      }
    for (int l=1;l<=n;l++)
     for (int x=1;x<=n-l+1;x++) {
        int y=x+l-1; tp=0;
        for (int i=1;i<=w;i++)
         for (int j=1;j<=h;j++) {
            for (int k=x;k<=y-1;k++) f[x][y][i][j]=min(f[x][y][i][j],f[x][k][i][j]+f[k+1][y][i][j]);
            if (f[x][y][i][j]!=INF) {
                q[++tp]=data(i,j); val[tp]=f[x][y][i][j]; 
              }
          }
        spfa(x,y);
     }
    int ans=INF;
    for (int i=1;i<=w;i++)
     for (int j=1;j<=h;j++) ans=min(ans,f[1][n][i][j]);
    printf("%d\n",(ans==INF)?-1:ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值