题目大意:给定一张地图,一些地方有障碍物,有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; }