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

来源:互联网 发布:淘宝客服昵称怎么修改 编辑:程序博客网 时间:2024/05/16 01:35

题目描述

传送门

题解

这道题改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方向推可以到达的位置,这个可以用记忆化+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 600003using 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 0
原创粉丝点击