BZOJ 3205 [Apio2013]机器人 斯坦纳树

这篇博客详细解析了Apio2013竞赛中的一道题目——机器人斯坦纳树。内容涉及如何解决机器人在网格上找到最短路径覆盖所有点的问题,重点探讨了斯坦纳树的概念及其应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

#include <cstdio>
#include <algorithm>
#include <queue>
#define N 505
#define M 600005
#define INF 1000000000
using namespace std;
const int xx[]={-1,0,1,0},yy[]={0,-1,0,1};
inline bool isdigit(const char& c) { return c>='0' && c<='9'; }
struct Point {
    int x,y;
    Point() {}
    Point(int _x,int _y):x(_x),y(_y) {}
    bool operator == (const Point& rhs) const { return x==rhs.x && y==rhs.y; }
}pos[10],to[N][N][4],s[N*N];
queue<Point> q1,q2;
int n,m,p,top,dfs_clock,f[10][10][N][N],mark[N][N][4];
char mp[N][N];
Point dfs(int x,int y,int dir) {
    if(mark[x][y][dir]==dfs_clock) return Point(-1,-1);
    mark[x][y][dir]=dfs_clock;
    if(to[x][y][dir].y) return to[x][y][dir];
    int ndir=dir;
    if(mp[x][y]=='A') ndir++;
    if(mp[x][y]=='C') ndir+=3;
    if(ndir>3) ndir-=4;
    int nx=x+xx[ndir], ny=y+yy[ndir];
    if(nx<1 || nx>n || ny<1 || ny>m || mp[nx][ny]=='x')
        return to[x][y][dir]=Point(x,y);
    return to[x][y][dir]=dfs(nx,ny,ndir);
}
void SPFA(int l,int r) {
    static bool inq[N][N];
    static int sum[M];
    static Point tmp[N*N];
    int maxx=-INF,minn=INF;
    for(int i=1;i<=top;i++) {
        int& val=f[l][r][s[i].x][s[i].y];
        sum[val]++;
        maxx=max(maxx,val), minn=min(minn,val);
        inq[s[i].x][s[i].y]=true;
    }
    for(int i=minn+1;i<=maxx;i++) sum[i]+=sum[i-1];
    for(int i=1;i<=top;i++) {
        int& val=f[l][r][s[i].x][s[i].y];
        tmp[sum[val]--]=s[i];
    }
    for(int i=1;i<=top;i++) s[i]=tmp[top-i+1];
    for(int i=minn;i<=maxx;i++) sum[i]=0;
    while(top) q1.push(s[top--]);
    while(!q1.empty() || !q2.empty()) {
        Point o;
        if(q1.empty()) o=q2.front(), q2.pop();
        else if(q2.empty()) o=q1.front(), q1.pop();
        else {
            Point o1=q1.front(),o2=q2.front();
            if(f[l][r][o1.x][o1.y]<f[l][r][o2.x][o2.y]) o=o1, q1.pop();
            else o=o2, q2.pop();
        }
        inq[o.x][o.y]=false;
        for(int i=0;i<4;i++) {
            Point no=to[o.x][o.y][i];
            if(no==Point(-1,-1) || f[l][r][o.x][o.y]+1>=f[l][r][no.x][no.y]) continue;
            f[l][r][no.x][no.y]=f[l][r][o.x][o.y]+1;
            if(!inq[no.x][no.y]) inq[no.x][no.y]=true, q2.push(no);
        }
    }
    return ;
}
int main() {
    scanf("%d%d%d",&p,&m,&n);
    for(int l=0;l<=p;l++)
        for(int r=0;r<=p;r++)
            for(int x=0;x<=n;x++)
                for(int y=0;y<=m;y++)
                    f[l][r][x][y]=INF;
    for(int i=1;i<=n;i++) scanf("%s",mp[i]+1);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) {
            if(!isdigit(mp[i][j])) continue;
            int x=mp[i][j]-'0';
            pos[x]=Point(i,j);
            f[x][x][i][j]=0;
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(mp[i][j]!='x')
                for(int k=0;k<4;k++)
                    dfs_clock++, to[i][j][k]=dfs(i,j,k);
    for(int i=1;i<=p;i++) s[++top]=pos[i], SPFA(i,i);
    for(int len=2;len<=p;len++)
        for(int l=1,r;(r=l+len-1)<=p;l++) {
            for(int x=1;x<=n;x++)
                for(int y=1;y<=m;y++) {
                    for(int k=l;k<r;k++)
                        f[l][r][x][y]=min(f[l][r][x][y],f[l][k][x][y]+f[k+1][r][x][y]);
                    if(f[l][r][x][y]!=INF)
                        s[++top]=Point(x,y);
                }
            SPFA(l,r);
        }
    int ans=INF;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            ans=min(ans,f[1][p][i][j]);
    if(ans==INF) ans=-1;
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值