BZOJ 3205 Apio2013 机器人 斯坦纳树

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


你可能感兴趣的:(bzoj,斯坦纳树,BZOJ3205)