本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

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 号机器人。

≤ 9,≤ 500 且   h ≤ 500

正解:斯坦纳树

解题报告:

  我已经醉了…这道题调了我半个晚上,一大堆傻逼错误,我已经被自己最近的低效状态感动了…

  看到这个题,很快可以想到斯坦纳树,不过这个题没必要把状态记录下来,因为一定是连续的区间。

  考虑用$f[l][r][i][j]$表示$l$到$r$的机器人在$(i,j)$这个地方合并的最小代价。

  然后转移的话就很简单了:

  $f[l][r][i][j]=min(f[l][r][p][q]+1,f[l][o][i][j]+f[o+1][r][i][j]) $,

  其中$(p,q)$可以一步走到$(i,j)$,$l<=o<r$。

  跟斯坦纳树的常规套路一样,$SPFA$转移即可。

  结果,这道题最臭不要脸的是卡!空!间!还卡!常!数!

  我把标号改为从$0$开始,并且把队列滚动,再把一系列空间开到下界,空间过了。

  然后我发现我在OJ上超时了...

  被BZOJ的评测机速度感动了...

  .............

  接下来就是各种预处理+常数优化,本机已经很快了,OJ上就是跑不过。

  .............

  最后怒写基数排序,终于跑过去了...基数排序大法好啊...

  代码已经看不得了...

BZOJ:

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
using namespace std;
typedef long long LL;
#define RG register
const int MAXN = 501;
const int MOD = 200000;
int n,m,k,f[10][10][MAXN][MAXN],to[4][MAXN][MAXN],inf,cnt,ans,pos[12][2];
char ch[MAXN+12][MAXN+12];
bool vis[4][MAXN][MAXN],use[MAXN][MAXN];
int head,tail,L,R,nex[MOD+12],val[10000],sum[10000];
int dx[4]={-1,0,1,0};
int dy[4]={0,-1,0,1};
//0:up 1:left 2:down 3:right
//顺时针:id+3 逆时针:id+1
struct node{ int x,y,dis; }tmp,a[MAXN*MAXN],dui[MOD+12],dui2[MOD+12],b[MAXN*MAXN];
inline bool cmp(node q,node qq){ return q.dis<qq.dis; }
inline int get(int x,int y){ return (x-1)*m+y; }
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
} inline int dfs(int l,int x,int y){//dfs预处理每个点沿着当前前进方向到达的地方,记忆化搜索
if(vis[l][x][y]) return to[l][x][y]=-1;
if(to[l][x][y]!=0) return to[l][x][y];
vis[l][x][y]=1; int savl=l;
if(ch[x][y]=='A') l++,l%=4;//逆时针
if(ch[x][y]=='C') l+=3,l%=4;//顺时针
int nowx=x+dx[l],nowy=y+dy[l];
if(nowx<=0 || nowy<=0 || nowx>n || nowy>m || ch[nowx][nowy]=='x') to[savl][x][y]=get(x,y);
else to[savl][x][y]=dfs(l,nowx,nowy);
vis[savl][x][y]=0;
return to[savl][x][y];//中间l发生了改变!必须另外保存!
} inline void SPFA(RG int l,RG int r){//带特技的SPFA,跟BFS有何区别呢...
memset(sum,0,sizeof(sum));
int mn,mx; mn=mx=a[1].dis;
for(int i=1;i<=cnt;i++) {
mn=min(mn,a[i].dis); mx=max(mx,a[i].dis);
sum[a[i].dis]++;
}
for(int i=mn+1;i<=mx;i++) sum[i]+=sum[i-1];
for(int i=1;i<=cnt;i++) b[sum[a[i].dis]--]=a[i];
for(int i=1;i<=cnt;i++) a[i]=b[i]; //sort(a+1,a+cnt+1,cmp);
head=tail=L=R=0;
for(int i=1;i<=cnt/*!!!*/;i++) tail=nex[tail],dui[tail]=a[i];
RG int ux,uy,nowx,nowy; RG node t1,t2;
while(head!=tail || L!=R) {
if(head==tail) L=nex[L],ux=dui2[L].x,uy=dui2[L].y;
else if(L==R) head=nex[head],ux=dui[head].x,uy=dui[head].y;
else {
t1=dui[nex[head]]; t2=dui2[nex[L]];//不能直接比较!需要用f比较!因为期间有可能被更新...
if(f[l][r][t1.x][t1.y]<f[l][r][t2.x][t2.y]) ux=t1.x,uy=t1.y,head=nex[head];
else ux=t2.x,uy=t2.y,L=nex[L];
}
for(RG int i=0;i<4;i++) {
if(to[i][ux][uy]<=0) continue;
nowx=(to[i][ux][uy]-1)/m+1; nowy=(to[i][ux][uy]-1)%m+1;
if(f[l][r][nowx][nowy]>f[l][r][ux][uy]+1) {
f[l][r][nowx][nowy]=f[l][r][ux][uy]+1;
if(!use[nowx][nowy]) {
use[nowx][nowy]=1; tmp.x=nowx; tmp.y=nowy; tmp.dis=f[l][r][nowx][nowy]/*!!!*/;
R=nex[R];
dui2[R]=tmp;
}
}
}
use[ux][uy]=0;
}
} inline void work(){
k=getint(); m=getint(); n=getint();
memset(f,0x3f,sizeof(f)); inf=f[0][0][0][0];
for(int i=1;i<=n;i++) { scanf("%s",ch[i]+1); for(int j=1;j<=m;j++) if(ch[i][j]<='9' && ch[i][j]>='1') f[ch[i][j]-'1'][ch[i][j]-'1'][i][j]=0; }
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(ch[i][j]!='x') {
for(int l=0;l<4;l++)
to[l][i][j]=dfs(l,i,j);
if(ch[i][j]<='9' && ch[i][j]>='1') pos[ch[i][j]-'1'][0]=i,pos[ch[i][j]-'1'][1]=j;
} for(int i=0;i<MOD;i++) nex[i]=(i+1)%MOD; for(int i=0;i<k;i++) {//一开始做一遍!
use[pos[i][0]][pos[i][1]]=0; cnt=0;
tmp.x=pos[i][0]; tmp.y=pos[i][1]; tmp.dis=0;
a[++cnt]=tmp;
SPFA(i,i);
} for(int l=k-2;l>=0;l--)
for(int r=l+1;r<k;r++) {
for(RG int ll=l;ll<r;ll++)
for(RG int i=1;i<=n;i++)
for(RG int j=1;j<=m;j++)
f[l][r][i][j]=min(f[l][r][i][j],f[l][ll][i][j]+f[ll+1][r][i][j]);
cnt=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(f[l][r][i][j]!=inf) {
use[i][j]=1; a[++cnt].x=i; a[cnt].y=j;
a[cnt/*!!!*/].dis=f[l][r][i][j];//记得加距离啊...
}
SPFA(l,r);
}
ans=inf;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(f[0][k-1][i][j]<ans) ans=f[0][k-1][i][j];
if(ans==inf) printf("-1"); else printf("%d",ans);
//cout<<endl<<clock()<<endl;
} int main()
{
work();
return 0;
}

  

UOJ:

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
using namespace std;
typedef long long LL;
#define RG register
const int MAXN = 501;
const int MOD = 300000;
int n,m,k,f[10][10][MAXN][MAXN],to[4][MAXN][MAXN],inf,cnt,ans,pos[12][2];
char ch[MAXN+12][MAXN+12];
bool vis[4][MAXN][MAXN],use[MAXN][MAXN];
int head,tail,L,R,nex[MOD+12],sum[700000];
int dx[4]={-1,0,1,0};
int dy[4]={0,-1,0,1};
//0:up 1:left 2:down 3:right
//顺时针:id+3 逆时针:id+1
struct node{ int x,y,dis; }tmp,a[MAXN*MAXN],dui[MOD+12],dui2[MOD+12],b[MAXN*MAXN];
inline bool cmp(node q,node qq){ return q.dis<qq.dis; }
inline int get(int x,int y){ return (x-1)*m+y; }
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
} inline int dfs(int l,int x,int y){//dfs预处理每个点沿着当前前进方向到达的地方,记忆化搜索
if(vis[l][x][y]) return to[l][x][y]=-1;
if(to[l][x][y]!=0) return to[l][x][y];
vis[l][x][y]=1; int savl=l;
if(ch[x][y]=='A') l++,l%=4;//逆时针
if(ch[x][y]=='C') l+=3,l%=4;//顺时针
int nowx=x+dx[l],nowy=y+dy[l];
if(nowx<=0 || nowy<=0 || nowx>n || nowy>m || ch[nowx][nowy]=='x') to[savl][x][y]=get(x,y);
else to[savl][x][y]=dfs(l,nowx,nowy);
vis[savl][x][y]=0;
return to[savl][x][y];//中间l发生了改变!必须另外保存!
} inline void SPFA(RG int l,RG int r){//带特技的SPFA,跟BFS有何区别呢...
memset(sum,0,sizeof(sum));
int mn,mx; mn=mx=a[1].dis;
for(int i=1;i<=cnt;i++) {
mn=min(mn,a[i].dis); mx=max(mx,a[i].dis);
sum[a[i].dis]++;
}
for(int i=mn+1;i<=mx;i++) sum[i]+=sum[i-1];
for(int i=1;i<=cnt;i++) b[sum[a[i].dis]--]=a[i];
for(int i=1;i<=cnt;i++) a[i]=b[i]; //sort(a+1,a+cnt+1,cmp);
head=tail=L=R=0;
for(int i=1;i<=cnt/*!!!*/;i++) tail=nex[tail],dui[tail]=a[i];
RG int ux,uy,nowx,nowy; RG node t1,t2;
while(head!=tail || L!=R) {
if(head==tail) L=nex[L],ux=dui2[L].x,uy=dui2[L].y;
else if(L==R) head=nex[head],ux=dui[head].x,uy=dui[head].y;
else {
t1=dui[nex[head]]; t2=dui2[nex[L]];//不能直接比较!需要用f比较!因为期间有可能被更新...
if(f[l][r][t1.x][t1.y]<f[l][r][t2.x][t2.y]) ux=t1.x,uy=t1.y,head=nex[head];
else ux=t2.x,uy=t2.y,L=nex[L];
}
for(RG int i=0;i<4;i++) {
if(to[i][ux][uy]<=0) continue;
nowx=(to[i][ux][uy]-1)/m+1; nowy=(to[i][ux][uy]-1)%m+1;
if(f[l][r][nowx][nowy]>f[l][r][ux][uy]+1) {
f[l][r][nowx][nowy]=f[l][r][ux][uy]+1;
if(!use[nowx][nowy]) {
use[nowx][nowy]=1; tmp.x=nowx; tmp.y=nowy; tmp.dis=f[l][r][nowx][nowy]/*!!!*/;
R=nex[R];
dui2[R]=tmp;
}
}
}
use[ux][uy]=0;
}
} inline void work(){
k=getint(); m=getint(); n=getint();
memset(f,0x3f,sizeof(f)); inf=f[0][0][0][0];
for(int i=1;i<=n;i++) { scanf("%s",ch[i]+1); for(int j=1;j<=m;j++) if(ch[i][j]<='9' && ch[i][j]>='1') f[ch[i][j]-'1'][ch[i][j]-'1'][i][j]=0; }
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(ch[i][j]!='x') {
for(int l=0;l<4;l++)
to[l][i][j]=dfs(l,i,j);
if(ch[i][j]<='9' && ch[i][j]>='1') pos[ch[i][j]-'1'][0]=i,pos[ch[i][j]-'1'][1]=j;
} for(int i=0;i<MOD;i++) nex[i]=(i+1)%MOD; for(int i=0;i<k;i++) {//一开始做一遍!
use[pos[i][0]][pos[i][1]]=0; cnt=0;
tmp.x=pos[i][0]; tmp.y=pos[i][1]; tmp.dis=0;
a[++cnt]=tmp;
SPFA(i,i);
} for(int l=k-2;l>=0;l--)
for(int r=l+1;r<k;r++) {
for(RG int ll=l;ll<r;ll++)
for(RG int i=1;i<=n;i++)
for(RG int j=1;j<=m;j++)
f[l][r][i][j]=min(f[l][r][i][j],f[l][ll][i][j]+f[ll+1][r][i][j]);
cnt=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(f[l][r][i][j]!=inf) {
use[i][j]=1; a[++cnt].x=i; a[cnt].y=j;
a[cnt/*!!!*/].dis=f[l][r][i][j];//记得加距离啊...
}
SPFA(l,r);
}
ans=inf;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(f[0][k-1][i][j]<ans) ans=f[0][k-1][i][j];
if(ans==inf) printf("-1"); else printf("%d",ans);
//cout<<endl<<clock()<<endl;
} int main()
{
work();
return 0;
}

  

BZOJ3205/UOJ107 [Apio2013]机器人的更多相关文章

  1. bzoj3205 [Apio2013]机器人

    3205: [Apio2013]机器人 Time Limit: 15 Sec  Memory Limit: 128 MBSubmit: 953  Solved: 227[Submit][Status] ...

  2. [Bzoj3205][Apio2013]机器人(斯坦纳树)(bfs)

    3205: [Apio2013]机器人 Time Limit: 15 Sec  Memory Limit: 128 MBSubmit: 977  Solved: 230[Submit][Status] ...

  3. bzoj千题计划230:bzoj3205: [Apio2013]机器人

    http://www.lydsy.com/JudgeOnline/problem.php?id=3205 历时一天,老子终于把它A了 哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈 因为不懂spfa ...

  4. [APIO2013]机器人(斯坦纳树)

    题目描述 VRI(Voltron 机器人学会)的工程师建造了 n 个机器人.任意两个兼容的机 器人站在同一个格子时可以合并为一个复合机器人. 我们把机器人用 1 至 n 编号(n ≤ 9).如果两个机 ...

  5. [APIO2013]机器人[搜索、斯坦纳树]

    题意 题目链接 分析 记 g(d,x,y) 表示从 (x,y) 出发,方向为 d 到达的点,这个可以通过记忆化搜索求出,注意如果转移成环(此时向这个方向走没有意义)要特判. 记 f(l,r,x,y) ...

  6. 【题解】APIO2013机器人

    其实这题前前后后的思考时间加起来应该有两天之久了,dp状态,转移方式等等都还是比较好想,然而左看右看觉得spfa复杂度未免太爆炸……然后选择看了一篇题解,发现在多重优化之下,其实是可以过的…… 首先建 ...

  7. [APIO2013]机器人

    题目描述 VRI(Voltron 机器人学会)的工程师建造了 n 个机器人.任意两个兼容的机 器人站在同一个格子时可以合并为一个复合机器人. 我们把机器人用 1 至 n 编号(n ≤ 9).如果两个机 ...

  8. bzoj 3205: [Apio2013]机器人【dfs+斯坦纳树+spfa】

    第一次听说斯坦纳树这种东西 先dfs预处理出来dis[i][j][k]表示格子(i,j)向k方向转移能到哪,记忆话搜索预处理,注意如果有环的话特判一下 设f[i][j][x][y]表示复合机器人i-j ...

  9. 【BZOJ3205_洛谷3638】[APIO2013]机器人(动态规划)

    题目: 洛谷3638 分析: 卡了一天的神题--(OrzJumpmelon) 首先预处理出从点\(p\)向\(d\)方向出发最终能到达的点\(nxt[p][d]\).这个可以直接记忆化搜索解决.如果出 ...

随机推荐

  1. NET Framework 4.5新特性 (三)64位平台支持大于2 GB大小的数组

    64位平台.NET Framework数组限制不能超过2GB大小.这种限制对于需要使用到大型矩阵和向量计算的工作人员来说,是一个非常大问题. 无论RAM容量有多大有多少,一旦你使用大型矩阵和向量计算工 ...

  2. ZOJ 3661 Palindromic Substring(回文树)

    Palindromic Substring Time Limit: 10 Seconds      Memory Limit: 65536 KB In the kingdom of string, p ...

  3. Windows Server 2008及以上系统磁盘无法查看(About UAC and ACE)

    在windows Server2008及以上系統,如果UAC Enabled,ACE列表中不會包含Administrators成員的SID,所以即使你是administrators的成員,也無法訪問D ...

  4. 百度 url 当在baidu搜索结果展示页,去点击标头时

    Spencer : 百度加一层跳转主要为了监控点击 w 基于dns和用户体验考虑的猜测 0-百度自己的cdn服务器存入各个域名/url的服务器ip(多ip情况下,返回物理空间相对用户最近的服务器ip) ...

  5. Struts 上传文件

    1. 客户端注意事项 method="post" enctype="multipart/form-data" <input type="file ...

  6. XP系统中IIS访问无法显示网页,目前访问网站的用户过多。终极解决办法

    无法显示网页 目前访问网站的用户过多. -------------------------------------------------------------------------------- ...

  7. JS让DIV绑定某个事件

    <html> <head> <title>Add/Remove Event Handlers Example</title> <script ty ...

  8. 快压、360压缩、WinRAR关于打开快压通过超高压缩比压缩后的文件不兼容的问题

    今天接收了同事发过来的一个压缩文件,用360压缩打开和用WinRAR打开压缩文件,傻眼了,这发的是什么鬼压缩包.压缩包的文件大小有27533KB,用360压缩工具解压查看只有121.5kb,而且完全没 ...

  9. Redis五(其他操作命令)

    其他常用操作 delete(*names) # 根据删除redis中的任意数据类型 exists(name) # 检测redis的name是否存在 keys(pattern='*') # 根据模型获取 ...

  10. C#数组的笔记

    Array.Copy的笔记: 1.将值类型的元素装箱位引用类型的元素,比如讲一个Int32[]的元素复制到Object[]中 2.将引用类型的元素拆箱为值类型的元素 3.加宽CLR基元值类型,比如讲一 ...