bzoj3205: [Apio2013]机器人

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3205

思路:类似斯坦纳树的想法

但是因为这里的合并必须连号

所以子集枚举就变成了区间合并

说说做法好了

首先记搜搜出每个点向四个方向走一步会到哪里

注意:转向器可能导致机器人一直在里面转出不来,要特判掉

然后设f[l][r][x][y]表示当前合并的机器人是[l,r],合并点是(x,y)

两种转移:

枚举子区间,合并f[l][r][x][y]=min(f[l][mid][x][y],f[mid+1][r][x][y])

从另一个地方转移过来f[l][r][x][y]=min(f[l][r][xx][yy]) (xx,yy)走一步能到(x,y)

然后还不够

第二种转移的spfa要加一个杂技优化

观察这个图 发现所有边的边权都是1 如果是单源的话SPFA可以进化成广搜

现在是多源 因此我们可以这样做:

维护两个队列,将初始所有的点按照距离排序后从小到大加入队列1

每次拓展出的点加入队列2

每次取出点的时候,如果队列1队尾元素的距离小于队列2 就把队列1的队尾元素拿去松弛 否则就用队列2的

这样做之后除了排序之外复杂度是线性的

排序的log可以用计数排序省掉,但是直接sort也能过,无妨

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

------以上内容来自popoqqq的博客

然后就卡过了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define id(x,y) id[x][y]
const int maxn=505,maxk=10,maxb=maxn*maxn,inf=1e9;
const int dx[]={-1,0,1,0};
const int dy[]={0,1,0,-1};
using namespace std;
int n,h,w,next[maxb][4],pos[maxk],f[maxk][maxk][250005],cnt,ans,*dist,id[maxn][maxn],q1[maxb*10],q2[maxb*10];
bool bo[maxb],vis[maxb][4];char map[maxn][maxn];
//inline int id(int x,int y){return x*(h+1)+y;}
inline bool ok(int x,int y){return x>0&&x<=w&&y>0&&y<=h&&map[x][y]!='x';}
inline void decode(int s,int &x,int &y){x=(s-1)/h+1,y=(s-1)%h+1;}
inline bool cmp(int x,int y){return dist[x]<dist[y];}

void dfs(int x,int y,int d){
	int s=id(x,y);
	if (vis[s][d]) return;
	vis[s][d]=1;
	next[s][d]=-1;
	int nx=x+dx[d],ny=y+dy[d];//printf("%d %d %d\n",x,y,d);
	if (!ok(nx,ny)) {next[id(x,y)][d]=id(x,y);return;}
	int newd=d;
	if (map[nx][ny]=='A') newd=(newd+3)%4;
	if (map[nx][ny]=='C') newd=(newd+1)%4;
	dfs(nx,ny,newd);
	next[s][d]=next[id(nx,ny)][newd];
}

void init(){
	scanf("%d%d%d",&n,&h,&w);
	for (int i=1;i<=w;i++){
		scanf("%s",map[i]+1);
		for (int j=1;j<=h;j++){
			id(i,j)=++cnt;
			if (isdigit(map[i][j])) pos[map[i][j]-'0']=id(i,j);
		//	printf("%d ",id[i][j]);
		}
	}
	//for (int i=1;i<=n;i++) printf("%d\n",pos[i]);
}

void bfs(int *d){
	int h1=0,t1=-1,h2=0,t2=-1;
	dist=d;
	for (int i=1;i<=cnt;i++) if (dist[i]!=inf) q2[++t2]=i;//printf("dist=%d\n",i);
	memset(bo,0,sizeof(bo));
	sort(q2,q2+1+t2,cmp);
	while (h1<=t1||h2<=t2){
		//puts("%p");
		int s,x,y;
		if (h1>t1) s=q2[h2++];
		else if (h2>t2) s=q1[h1++];
		else{
			if (dist[q1[h1]]<dist[q2[h2]]) s=q1[h1++];
			else s=q2[h2++];
		}
		decode(s,x,y);//printf("%d %d %d\n",s,x,y);
		bo[s]=1;
		for (int i=0;i<4;i++){
			int ns=next[s][i];
			if (ns!=-1&&dist[ns]>dist[s]+1){
				dist[ns]=dist[s]+1;
				bo[ns]=1,q1[++t1]=ns;
			}
		}
		while (h2<=t2&&bo[q2[h2]]) h2++;
	}
}

void work(){
	for (int i=1;i<=w;i++)
		for (int j=1;j<=h;j++)
			if (map[i][j]!='x')
				for (int d=0;d<4;d++)
					dfs(i,j,d);
	//dfs(1,1,3);for (;;);
/*	for (int k=0;k<4;k++,puts("\n"))
	for (int i=1;i<=w;i++,puts(""))
		for (int j=1;j<=h;j++){
			//int x,y;decode(next[id(i,j)][k],x,y);
			printf("%d ",next[id(i,j)][k]);
	//		printf("(%d,%d) ",x,y);
		}*/
	for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            for (int k=1;k<=cnt;k++) f[i][j][k]=inf;
	for (int i=1;i<=n;i++)
		f[i][i][pos[i]]=0;
	for (int len=1;len<=n;len++)
        for (int l=1;l+len-1<=n;l++) {
            int r=l+len-1;
            for (int mid=l;mid<r;mid++)
                for (int p=1;p<=cnt;p++) f[l][r][p]=min(f[l][r][p],f[l][mid][p]+f[mid+1][r][p]);
            //printf("%d ",f[l][r][1]);
            //printf("%d %d\n\n\n\n",l,r);
            bfs(f[l][r]);
        }
	ans=inf;
	for (int i=1;i<=cnt;i++) ans=min(ans,f[1][n][i]);
	printf("%d\n",ans==inf?-1:ans);
}

int main(){
	init(),work();
	return 0;
}
/*
4 10 5
1.........
AA...x4...
..A..x....
2....x....
..C.3.A...
*/
时间: 2024-11-06 07:10:59

bzoj3205: [Apio2013]机器人的相关文章

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

http://www.lydsy.com/JudgeOnline/problem.php?id=3205 历时一天,老子终于把它A了 哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈 因为不懂spfa的优化 以及 数组越界  TAT ┭┮﹏┭┮ 牢骚发完了,题解在下面  (⊙o⊙)… n只有9,很像状压dp dp[l][r][x][y] 表示在(x,y)位置 合成了x-y复合机器人 的最少推动次数 它的转移 存在后效性 所以上 斯坦纳树 自身的转移:dp[l][r][x][y]=min{dp[l

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

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

题目描述 VRI(Voltron 机器人学会)的工程师建造了 n 个机器人.任意两个兼容的机 器人站在同一个格子时可以合并为一个复合机器人. 我们把机器人用 1 至 n 编号(n ≤ 9).如果两个机器人的编号是连续的,那 么它们是兼容的,可以合并成一个复合机器人.最初这 n 个机器人各自都只有唯 一的编号.而一个由两个或以上的机器人合并构成的复合机器人拥有两个编号, 分别是构成它的所有机器人中最小和最大的编号. 例如,2 号机器人只可以与 1 号或 3 号机器人合并.若 2 号机器人与 3 号

Luogu P3638 [APIO2013]机器人

(类似)斯坦纳树+DP \(f[l][r][i][j]\) 表示已经构成 \([l,r]\) 的机器人,并在点 \((i,j)\) 的最小代价. 预处理出 \(d[i][j][k]\) 表示在点 \((i,j)\) 方向为 \(k\) 时最终能够到达的点. \(f[l][r][i][j]=\min(f[l][k][i][j],f[k+1][r][i][j])\) \(枚举k,f[l][r][X][Y]=\min(f[l][r][X][Y],f[l][r][i][j]+1),(X,Y)表示(i,j

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

腊鸡题目,实在卡不过去. (改了一下午) 就是裸的斯坦纳树的题目,一方面合并子集,另一方面SPFA迭代求解. 优化了许多地方,甚至基数排序都写了. 还是T到死,不打算改了,就这样吧 #include <map> #include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm

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

斯坦纳树 好神啊……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]

这是一款借助chrome 插件的微信机器人

1.chrome kit微信机器人简介(github:https://github.com/LinuxForYQH/chrome_kit) 借助chrome 插件 js注入来实现消息的发送 chrome devtool api的调用来监听https请求 打开微信登录界面,在扫码登录前必须先打开toolbar(F12 或者 鼠标右键检查),如上所说因为借助了chrome devtool api所以需要打开toolbar才能执行相关dev域的js. 2.相关开发原理介绍 https://develo

机器人--推荐系统(1)

430的目标是完成机器人的推荐系统,提高机器人回答问题的准确率,关于过程碰到的问题以及解决方案与大家分享一下,(请轻喷!) 那么这个推荐系统到底应该怎么做呢? 最开始的第一个思路是 根据用户 进入到ERP的模块 推荐该模块下的相关问题.其实就是根据用户的轨迹来推荐问题,这是一个思路但是不太完整.因为很有可能用户就从ERP的桌面就进入了机器人,但他实际要咨询的是销售系统的相关知识,那么此时的推荐就不太满足用户的咨询需求. 因此参考一些业界其他公司的做法,可以基于内容的协同过滤(Collaborat

ROS turtlebot_follower :让机器人跟随我们移动

ROS turtlebot_follower 学习 首先在catkin_ws/src目录下载源码,地址:https://github.com/turtlebot/turtlebot_apps.git 了解代码见注释(其中有些地方我也不是很明白) follower.cpp #include <ros/ros.h> #include <pluginlib/class_list_macros.h> #include <nodelet/nodelet.h> #include &