题目大意:给定一张地图,一些地方有障碍物,有k<=9个机器人,可以一推到底,遇到转向器会转向,两个编号相邻的机器人可以合并,求最少推多少次可以全部合并
令f[l][r][i][j]表示在点(i,j)将编号在[l,r]区间内的机器人全部合并的最小推动次数
则有动规方程组:
f[l][r][i][j]=min{f[l][r][_i][_j]+1} ( (_i,_j)->(i,j) )
f[l][r][i][j]=min(f[l][temp][i][j]+f[temp+1][r][i][j]) (l<=temp<r)
我们首先用记忆化搜索处理出每个点向四个方向推动后会到哪
然后利用根据这两个方程跑斯坦纳树即可
下面是细节部分:
1.转向器有环 因此会无限递归爆系统栈 标记一下就好
2.处理上面那个之后本机测还是会爆系统栈 没事交上去就不爆了
3.SPFA有一个优化 不加会T
观察这个图 发现所有边的边权都是1 如果是单源的话SPFA可以进化成广搜
现在是多源 因此我们可以这样做:
维护两个队列,将初始所有的点按照距离排序后从小到大加入队列1
每次拓展出的点加入队列2
每次取出点的时候,如果队列1队尾元素的距离小于队列2 就把队列1的队尾元素拿去松弛 否则就用队列2的
这样做之后除了排序之外复杂度是线性的
排序的log可以用计数排序省掉,但是直接sort也能过,无妨
然后这题就搞掉了。。。。。。
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 510
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int> abcd;
template<typename T>class Reader{
private:
T memory[M][M];
public:
T& operator [] (const abcd &x)
{
return memory[x.first][x.second];
}
T* operator [] (int x)
{
return memory[x];
}
};
int m,n,k,T;
Reader<char> map;
Reader<int> f[10][10];
//f[l][r][i][j]表示编号为[l,r]的机器人在(i,j)上的最小花销
Reader<abcd[4]> aim;
Reader<int[4]> mark;
//0-上 1-左 2-下 3-右
//A +1 %4
//C +3 %4
queue<abcd> q1,q2;
Reader<bool> v;
abcd pos[10];
abcd stack[M*M];int top;
int L,R;
bool Compare(const abcd &x,const abcd &y)
{
return f[L][R][x] > f[L][R][y] ;
}
bool operator ! (const abcd &x)
{
return x.first==0 && x.second==0 ;
}
abcd Memorial_Search(int x,int y,int dir)
{
static const int dx[]={-1,0,1,0};
static const int dy[]={0,-1,0,1};
static int xx,yy;
if(mark[x][y][dir]==T)
return abcd(-1,-1);
mark[x][y][dir]=T;
if(!!aim[x][y][dir])
return aim[x][y][dir];
abcd& re=aim[x][y][dir];
if(map[x][y]=='A')
dir=(dir+1)%4;
if(map[x][y]=='C')
dir=(dir+3)%4;
xx=x+dx[dir];yy=y+dy[dir];
if(xx<=0||yy<=0||xx>m||yy>n||map[xx][yy]=='x')
return re=abcd(x,y);
return re=Memorial_Search(xx,yy,dir);
}
void SPFA(int l,int r)
{
int dir;
abcd x;
L=l;R=r;sort(stack+1,stack+top+1,Compare);
while(top)
q1.push(stack[top--]);
while( !q1.empty() || !q2.empty() )
{
if( q1.empty() )
x=q2.front(),q2.pop();
else if( q2.empty() )
x=q1.front(),q1.pop();
else if( f[l][r][q1.front()] < f[l][r][q2.front()] )
x=q1.front(),q1.pop();
else
x=q2.front(),q2.pop();
v[x]=0;
for(dir=0;dir<4;dir++)
{
abcd y=aim[x.first][x.second][dir];
if(y.first==-1&&y.second==-1)
continue;
if(f[l][r][y]>f[l][r][x]+1)
{
f[l][r][y]=f[l][r][x]+1;
if(!v[y])
v[y]=1,q2.push(y);
}
}
}
}
void Steiner_Tree()
{
int l,r,len,i,j,temp;
for(i=1;i<=k;i++)
{
stack[++top]=pos[i];
SPFA(i,i);
}
for(len=2;len<=k;len++)
for(l=1;(r=l+len-1)<=k;l++)
{
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
{
for(temp=l;temp<r;temp++)
f[l][r][i][j]=min(f[l][r][i][j],f[l][temp][i][j]+f[temp+1][r][i][j]);
if(f[l][r][i][j]!=INF)
{
abcd temp(i,j);
stack[++top]=temp;
v[temp]=1;
}
}
SPFA(l,r);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("robot.in","r",stdin);
freopen("robot.out","w",stdout);
#endif
int i,j,dir;
cin>>k>>n>>m;
memset(&f,0x3f,sizeof f);
for(i=1;i<=m;i++)
{
static char s[M];
scanf("%s",s+1);
for(j=1;j<=n;j++)
{
map[i][j]=s[j];
if( map[i][j]>'0' && map[i][j]<='9' )
{
int temp=map[i][j]-'0';
f[temp][temp][i][j]=0;
pos[temp]=abcd(i,j);
}
}
}
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
if(map[i][j]!='x')
for(dir=0;dir<4;dir++)
++T,Memorial_Search(i,j,dir);
Steiner_Tree();
int ans=INF;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
ans=min(ans,f[1][k][i][j]);
if(ans==INF) ans=-1;
cout<<ans<<endl;
return 0;
}