【BZOJ】【3205】【APIO2013】机器人robot

时间:2021-04-09 10:02:29

斯坦纳树


  好神啊……Orz zyf && PoPoQQQ

  为啥跟斯坦纳树扯上关系了?我想是因为每个点(robot)都沿着树边汇到根的时候就全部合起来了吧= =这个好像和裸的斯坦纳树不太一样,那个是无向最小生成树,这个是有向图……

  引用题解:

令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也能过,无妨

然后这题就搞掉了。。。。。。

但是蒟蒻的快排程序就TLE了啊……然后用计数排序的MLE了QAQ

只能献上我的膝盖

 //APIO 2013 robots
#include<queue>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define rep(i,n) for(int i=0;i<n;++i)
#define F(i,j,n) for(int i=j;i<=n;++i)
#define D(i,j,n) for(int i=j;i>=n;--i)
#define pb push_back
using namespace std;
typedef long long LL;
inline int getint(){
int r=,v=; char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-')r=-;
for(; isdigit(ch);ch=getchar()) v=v*+ch-'';
return r*v;
}
const int N=,INF=1e9;
#define debug
/*******************template********************/
const int fx[]={,,-,},
fy[]={,,,-};//down,right,up,left
int n,w,h,mp[N][N],vis[N][N][],ti;
char s[N];
struct pii{int X,Y;}G[N][N][],b[N*N];
queue<pii> q1,q2;
bool inq[N][N];
int f[N][N][][];
pii dfs(int x,int y,int k){
if (vis[x][y][k]==ti) return (pii){,};
if (vis[x][y][k]) return G[x][y][k];
vis[x][y][k]=ti;
int kk=k;
if (mp[x][y]==) kk=(k+)%;
else if (mp[x][y]==) kk=(k+)%;
int tx=x+fx[kk],ty=y+fy[kk];
if (mp[tx][ty]==) return G[x][y][k]=(pii){x,y};
return G[x][y][k]=dfs(tx,ty,kk);
}/*
vector<pii>E[N*N];
void count_sort(pii *b,int n,int l,int r){
int mx=0;
rep(i,N*N) E[i].clear();
F(i,1,n){
E[f[b[i].X][b[i].Y][l][r]].pb(b[i]);
mx=max(mx,f[b[i].X][b[i].Y][l][r]);
}
int tot=0;
F(i,0,mx) rep(j,E[i].size())
b[++tot]=E[i][j];
}*/
int l,r;
bool cmp(pii a,pii b){
return f[a.X][a.Y][l][r]<f[b.X][b.Y][l][r];
}
int main(){
#ifndef ONLINE_JUDGE
freopen("robot.in","r",stdin);
freopen("robot.out","w",stdout);
#endif
n=getint(); w=getint(); h=getint();
F(i,,h){
scanf("%s",s+);
F(j,,w){
F(l,,n) F(r,l,n) f[i][j][l][r]=INF;
if (s[j]=='x') mp[i][j]=;
else if (s[j]=='A') mp[i][j]=;
else if (s[j]=='C') mp[i][j]=;
else if (s[j]=='.') mp[i][j]=;
else {int t=mp[i][j]=s[j]-''+; f[i][j][t-][t-]=;}
}
}
#define FOR F(i,1,h)F(j,1,w)
FOR if (mp[i][j]) rep(k,) ++ti,dfs(i,j,k);
F(len,,n)
for(l=,r=l+len-;r<=n;l++,r++){
int tot=;
FOR{
F(k,l,r-) f[i][j][l][r]=min(f[i][j][l][r],f[i][j][l][k]+f[i][j][k+][r]);
if (f[i][j][l][r]!=INF) b[++tot]=(pii){i,j},inq[i][j]=;
}
// count_sort(b,tot,l,r);
sort(b+,b+tot+,cmp);
F(i,,tot) q1.push(b[i]);
while(!q1.empty()||!q2.empty()){
pii t;
if (q1.empty()) t=q2.front(),q2.pop();
else if (q2.empty()) t=q1.front(),q1.pop();
else{
pii t1=q1.front(),t2=q2.front();
if (f[t1.X][t1.Y][l][r]<f[t2.X][t2.Y][l][r]) t=t1,q1.pop();
else t=t2,q2.pop();
}
int x=t.X,y=t.Y,tx,ty; inq[x][y]=;
rep(i,){
tx=G[x][y][i].X,ty=G[x][y][i].Y;
if (!tx||!ty) continue;
if (f[tx][ty][l][r]>f[x][y][l][r]+){
f[tx][ty][l][r]=f[x][y][l][r]+;
if (!inq[tx][ty]){
inq[tx][ty]=;
q2.push((pii){tx,ty});
}
}
}
}
}
int ans=INF;
FOR ans=min(ans,f[i][j][][n]);
printf("%d\n",ans==INF?-:ans);
return ;
}

3205: [Apio2013]机器人

Time Limit: 15 Sec  Memory Limit: 128 MB
Submit: 237  Solved: 49
[Submit][Status][Discuss]

Description

VRI(Voltron
机器人学会)的工程师建造了 n个机器人。任意两个兼容的机器人站在同一个格子时可以合并为一个复合机器人。我们把机器人用 1至 n编号(n ≤
9)。如果两个机器人的编号是连续的,那么它们是兼容的,可以合并成一个复合机器人。最初这   n  
个机器人各自都只有唯一的编号。而一个由两个或以上的机器人合并构成的复合机器人拥有两个编号,分别是构成它的所有机器人中最小和最大的编号。例如,
2号机器人只可以与 1号或 3号机器人合并。若 2号机器人与 3号机器人合并,可构成编号为 2-3的复合机器人。如果编号为
2-3的复合机器人与编号为 4-6的复合机器人合并,可构成编号为 2-6的复合机器人。当所有机器人合并以后则构成 1-n复合机器人。工程师把这
n个机器人放在了一个封闭的房间中,房间四周均是墙。该房间被划分成 w     h   
个方格。有些方格有障碍物,机器人不可经过或停留;其余方格允许多个机器人停留,同时允许机器人经过。任何时候一个机器人只占用一个方格。初始时刻,所有
机器人均在不同的方格中。这些原始的机器人不会自发地移动。它们只有被工程师沿   x轴或
y轴推动后,才会沿推动的方向不断向前直线移动,直至碰到障碍物或墙停止移动。停止移动后,它会扫描当前的格子是否存在可以与它合并的机器人,如果有,则
合并并继续检查,直至不能再合并为止。工程师只能沿水平向左、水平向右、竖直向上、竖直向下四个方向推动机器人,并且,在机器人尚未停止移动时,不允许推
动其它机器人,因此任何时刻,房间中都只能有一个机器人移动,为了帮助机器人转向,工程师在一些格子中放置了转向器。具体地说,转向器分为顺时针转向器
(右转器)和逆时针转向器(左转器),顺时针转向器可以使到达该格子的机器人沿顺时针方向转向  
90_;逆时针转向器可以使到达该格子的机器人沿逆时针方向转向
90_。现在,我们将告诉你初始时刻房间内的信息。请你计算工程师最少共计需要推动机器人多少次,才能把所有的 n个机器人全部合并(如果可能的话)。

Input

你的程序必须从标准输入读入。输入的第 1行包含 3个整数 n、w和 h,用空格隔开。输入文件中接下来的 h行描述初始时刻房间内的信息,每行包含w个字符。这w* h 字符中每一个表示房间中的一个格子,意义如下:
 
‘ 1’至‘9’:表示该方格中有一个机器人,编号为这个数字;
‘ x’:表示该方格有障碍物;
 
‘ A’:表示该方格中有一个逆时针转向器;
 
‘ C’:表示该方格中有一个顺时针转向器;
‘ .’:表示该方格为空地。

Output

你的程序必须输出到标准输出。输出仅一个整数,表示最少需要推动的次数。
若不能使所有机器人全部合并,输出-1。

Sample Input

4 10 5
1.........
AA...x4...
..A..x....
2....x....
..C.3.A...

Sample Output

5

HINT

第一步:向右推动 3 号机器人,当它碰到转向器后会向上继续移动,直至碰到墙壁停止移动。第二步:向上推动 4 号机器人,当它碰到墙壁后停止移动,与3 号机器人合并,构成  3-4 号机器人 第三步:向上推动 2 号机器人,当它碰到转向器后会向左移动,由于左侧为墙壁,故停留在原地。第四步:向右推动  2 号机器人,由于它在一个转向器上,故它会向上移动,直至碰到墙壁停止移动,与  1 号机器人合并,构成 1-2 号机器人。第五步:向左推动  3-4 号机器人,当它碰到墙壁后停止移动,与 1-2 号机器人合并,构成  1-4 号机器人。

n ≤ 9,w ≤ 500且   h ≤ 500

Source

[Submit][Status][Discuss]