题目描述
传送门
题解
这道题改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);
}