【bzoj3205】[Apio2013]机器人 斯坦纳树

f[l][r][i][j]表示把l到r的机器人合并,停在(i,j)点的最小步数

f[l][r][i][j]=min{f[l][k][i][j]+f[k+1][r][i][j]}

f[l][r][i][j]=min{f[l][r][i'][j']+1}

记忆化搜索一下每个点从每个方向出发会走到哪里

注意,题目里可能出现环,要特判

spfa要加上一个优化才能通过

先把初始队列中的点按距离排序,每次扩展的点放在新队列里,取新队列和初始队列队尾较小的点进行扩展


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define maxn 510
#define inf 1000000000

using namespace std;

struct yts
{
	int x,y,dis;
}q1[510*510],q2[510*510];

bool cmp(yts x,yts y) {return x.dis<y.dis;}

int dx[4]={-1,0,1,0};
int dy[4]={0,-1,0,1};
int d[4][maxn][maxn];
char s[maxn][maxn];
int dp[10][10][maxn][maxn];
bool vis[maxn][maxn],tag[4][maxn][maxn];
int n,w,h;
//0上1左2下3右

int cal(int x,int y) {return (x-1)*w+y;}

int dfs(int k,int x,int y)
{
	if (x<1 || x>h || y<1 || y>w) return 0;
	if (s[x][y]=='x') return 0;
	if (d[k][x][y]) return d[k][x][y];
	if (tag[k][x][y]) return -1;
	tag[k][x][y]=1;
	int _k=k;
	if (s[x][y]=='A') _k=(k+1)%4;
	if (s[x][y]=='C') _k=(k+3)%4;
	d[k][x][y]=dfs(_k,x+dx[_k],y+dy[_k]);
	if (!d[k][x][y]) d[k][x][y]=cal(x,y);
	tag[k][x][y]=0;
	return d[k][x][y];
}

int main()
{
	scanf("%d%d%d",&n,&w,&h);
	for (int i=1;i<=h;i++) scanf("%s",s[i]+1);
	for (int i=1;i<=h;i++)
	  for (int j=1;j<=w;j++)
	    for (int k=0;k<4;k++)
	      d[k][i][j]=dfs(k,i,j);
	for (int l=1;l<=n;l++)
	  for (int r=1;r<=n;r++)
	    for (int x=1;x<=h;x++)
	      for (int y=1;y<=w;y++)
	        dp[l][r][x][y]=inf;
	for (int x=1;x<=h;x++)
	  for (int y=1;y<=w;y++)
	    if (s[x][y]-'0'>=1 && s[x][y]-'0'<=n) dp[s[x][y]-'0'][s[x][y]-'0'][x][y]=0;
	for (int l=n;l>=1;l--)
	  for (int r=l;r<=n;r++)
	  {
	  	for (int x=1;x<=h;x++)
	  	  for (int y=1;y<=w;y++)
	  	    for (int k=l;k<r;k++)
	  	      dp[l][r][x][y]=min(dp[l][r][x][y],dp[l][k][x][y]+dp[k+1][r][x][y]);
	  	memset(vis,0,sizeof(vis));
	  	int L=0,R=0;
	  	int l1=0,r1=0;
	  	for (int x=1;x<=h;x++)
	  	  for (int y=1;y<=w;y++)
	  	    if (dp[l][r][x][y]!=inf) q1[++R]=(yts){x,y,dp[l][r][x][y]},vis[x][y]=1;
	  	sort(q1+1,q1+R+1,cmp);
	  	while (L!=R || l1!=r1)
	  	{
	  		int x,y;
	  		if (L==R || (l1!=r1 && dp[l][r][q1[L].x][q1[L].y]>dp[l][r][q2[l1].x][q2[l1].y]))
	  		{
	  			l1++;if (l1==500*500) l1=0;
	  			x=q2[l1].x;y=q2[l1].y;
	  		}
	  		else
	  		{
	  			L++;if (L==500*500) L=0;
	  			x=q1[L].x;y=q1[L].y;
	  		}
	  	 	for (int k=0;k<4;k++)
	  	 	{
	  	 		int xx=(d[k][x][y]-1)/w+1,yy=(d[k][x][y]-1)%w+1;
	  	 		if (dp[l][r][xx][yy]>dp[l][r][x][y]+1)
	  	 		{
	  	 			dp[l][r][xx][yy]=dp[l][r][x][y]+1;
	  	 			if (!vis[xx][yy])
	  	 			{
	  	 				r1++;if (r1==500*500) r1=0;
	  	 				q2[r1]=(yts){xx,yy,dp[l][r][xx][yy]};
	  	 			}
	  	 		}
	  	 	}
	  	 	vis[x][y]=0;
	  	}
	  }
	int ans=inf;
	for (int x=1;x<=h;x++)
	  for (int y=1;y<=w;y++)
	    ans=min(ans,dp[1][n][x][y]);
	if (ans==inf) puts("-1"); else printf("%d\n",ans);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值