[APIO2013]机器人

机器人

题解

**卡常题

首先,由于一个机械人只能往 4 4 4个方向走,而每个点往一个方向走最终停留下来的点是固定的,我们可以考虑每个点往每个方向走走到的点先预处理出来,建个新图。

由于它要求只有编号连续的机械人在同一个节点上是才能够合并,所以我们可以考虑区间 d p dp dp
定义 d p l , r , i , j dp_{l,r,i,j} dpl,r,i,j表示编号区间为 [ l , r ] [l,r] [l,r]的机械人走到格子 ( i , j ) (i,j) (i,j)上时最小的代价,容易得到 d p dp dp转移方程式:
d p l , r , i , j = min ⁡ k = l r − 1 ( d p l , k , i , j + d p k + 1 , r , i , j , d p l , r , i , j ) dp_{l,r,i,j}=\min_{k=l}^{r-1}\left(dp_{l,k,i,j}+dp_{k+1,r,i,j},dp_{l,r,i,j}\right) dpl,r,i,j=k=lminr1(dpl,k,i,j+dpk+1,r,i,j,dpl,r,i,j)
d p l , r , t o i , j , k = min ⁡ ( d p l , r , t o i , j , k , d p l , r , i , j + 1 ) dp_{l,r,to_{i,j,k}}=\min\left(dp_{l,r,to_{i,j,k}},dp_{l,r,i,j}+1\right) dpl,r,toi,j,k=min(dpl,r,toi,j,k,dpl,r,i,j+1)
第一个式子主要涉及同一个节点上区间的合并,而第二个式子这是机械人在图上得移动。
对于第一个式子,直接转移即可。
对于第二个式子,这是一个多元的最短路,我们可以考虑用 S P F A SPFA SPFA来进行维护。
直接对于每个 ( l , r ) (l,r) (l,r)暴力进行区间转移与最短路即可。

但不幸的时,直接暴力 S P F A SPFA SPFA是会 T T T飞的,考虑优化。
然后我们就要开始加上一些稀奇古怪的优化了。
L L L LLL LLL S L F SLF SLF优化当然是必须的,不过实际上 L L L LLL LLL好像没多大用 (指算法)
我们在将 S P F A SPFA SPFA的起始点加入队列时需要先将其按 d p l , r , i , j dp_{l,r,i,j} dpl,r,i,j的值排序,再加入队列,以保证最开始的起点竟可能的小。
然后就是 S P F A SPFA SPFA选起点时可以选择队列前两个点中较小的一个,将大的放回队首。
注意 L L L LLL LLL优化记录 s u m sum sum时要开 l o n g l o n g longlong longlong,否则会在 u o j uoj uoj上挂掉。

然后,统计答案即可。
时间复杂度达到了惊人的 O ( n 3 h w + n 2 ( h w ) 2 ( ? ) ) O\left(n^3hw+n^2(hw)^2(?)\right) O(n3hw+n2(hw)2(?))
话说这能过真的正常吗?

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 300005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int orG=3,invG=332748118;
const int lim=300000;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y){return x+y<mo?x+y:x+y-mo;}
int qkpow(int a,int s){int t=1;while(s){if(s&1)t=1ll*a*t%mo;a=1ll*a*a%mo;s>>=1;}return t;}
int dx[5]={1,0,-1,0},dy[5]={0,1,0,-1};
int n,w,h,ans,l,r,to[MAXN][5];
int dp[12][12][MAXN];char maze[505][505];
int head,tail,stak,q[MAXN],sta[MAXN];
bool vis[5][505][505],inq[MAXN];
inline int Hash(pii x){return (x.fir-1)*w+x.sec;}
int dosaka(int x,int y,int f){
	int id=Hash(mkpr(x,y));if(vis[f][x][y])return to[id][f];
	to[id][f]=Hash(mkpr(-1,-1));vis[f][x][y]=1;int tf=f;
	if(maze[x][y]=='A')f=f>2?f-3:f+1;
	if(maze[x][y]=='C')f=f>0?f-1:f+3;
	int tx=x+dx[f],ty=y+dy[f];
	if(tx<1||ty<1||tx>h||ty>w)return to[id][tf]=id;
	if(maze[tx][ty]=='x')return to[id][tf]=id;
	return to[id][tf]=dosaka(tx,ty,f);
}
bool cmp(int x,int y){return dp[l][r][x]<dp[l][r][y];}
inline int adhead(){int tp=head;head++;if(head>lim)head=1;return tp;}
inline int adtail(){int tp=tail;tail++;if(tail>lim)tail=1;return tp;}
inline int rehead(){head--;if(head<1)head=lim;return head;}
signed main(){
	read(n);read(w);read(h);ans=INF;
	for(reg int i=1;i<=h;++i)scanf("\n%s",maze[i]+1);
	for(int l=1;l<=n;l++)
		for(int r=l;r<=n;r++)
			for(int i=1;i<=h*w;i++)
				dp[l][r][i]=INF;
	for(reg int i=1;i<=h;++i)
		for(reg int j=1;j<=w;++j){
			if(maze[i][j]=='x')continue;for(reg int k=0;k<4;++k)to[Hash(mkpr(i,j))][k]=dosaka(i,j,k);
			if('1'<=maze[i][j]&&maze[i][j]<='9'){int x=maze[i][j]-'0';dp[x][x][Hash(mkpr(i,j))]=0;}	
		}
	for(reg int len=1;len<=9;++len)
		for(l=1,r=len;r<=n;++l,++r){
			stak=0;head=tail=1;
			for(reg int i=1;i<=h*w;++i){
				for(reg int k=l;k<r;++k)
					dp[l][r][i]=min(dp[l][r][i],dp[l][k][i]+dp[k+1][r][i]);
				inq[i]=0;if(dp[l][r][i]<INF-1)sta[++stak]=i;
			}
			LL sum=0;int cnt=0;sort(sta+1,sta+stak+1,cmp);
			for(reg int i=1;i<=stak;++i)q[tail++]=sta[i],cnt++,sum+=1ll*dp[l][r][sta[i]];
			while(head!=tail){
				int t=q[adhead()],tp=dp[l][r][t];
				if(head!=tail){int t2=q[adhead()];if(dp[l][r][t2]<tp)swap(t,t2),tp=dp[l][r][t];q[rehead()]=t2;}
				if(1ll*cnt*tp>sum){q[adtail()]=t;continue;}cnt--;sum-=1ll*tp;inq[t]=0;int tmp=head!=tail?dp[l][r][q[head]]:INF;
				for(reg int k=0;k<4;++k){
					const int v=to[t][k];if(v<0)continue;
					if(dp[l][r][v]>tp+1){
						dp[l][r][v]=tp+1;
						if(!inq[v]){
							inq[v]=1;cnt++;sum+=1ll*dp[l][r][v];
							if(dp[l][r][v]>tmp)q[adtail()]=v;	
							else q[rehead()]=v,tmp=dp[l][r][v];
						}
					}
				}
			}
		}
	for(reg int i=1;i<=h*w;++i)ans=min(ans,dp[1][n][i]);
	printf("%d\n",ans<INF-1?ans:-1);
	return 0; 
} 

谢谢!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值