NOI Online #2 入门组 T2 荆轲刺秦王

原题链接

简洁题意:

S S S T T T,单位时间可以走格子周围的八个格子,有些格子不能走,这些格子周围距离小于等于 a ( i , j ) a(i,j) a(i,j)的格子只能用一次技能一才能走。每个时刻可以用技能二,单位时间向上下左右走 d d d格。问最快多久,若多种方案则希望技能用得少,技能用的一样多则希望少用技能一,无解输出 − 1 -1 1,反之输出时间和各个技能用的数量。

分析:

数据范围不大,可以用 b f s bfs bfs,直到答案大于当前答案或者不能走就停止,到达 T T T后对比答案并更新,周围能观察到的点就搜索覆盖。但是这样仍然会超时,因为我们处理一个点所能影响的周围的点太慢了。不难发现这个是一个菱形,可以针对每一个菱形的每一行进行差分,最后统一处理。注意在处理字符时 a ( i , j ) a(i,j) a(i,j)有可能有两位。
差分最后统计的时间复杂度是 Θ ( n m ) \Theta(nm) Θ(nm)的, b f s bfs bfs每个点都出入队一次,时间复杂度 Θ ( n m ) \Theta(nm) Θ(nm),总共最多枚举 n 2 m 2 n^2m^2 n2m2条边,所以总的时间复杂度是 O ( n 2 m 2 ) O(n^2m^2) O(n2m2)的。这样时间会刚好超一点,但是实际使用起来是不可能出现每两个点之间都可以互相到达的情况, n 2 m 2 n^2m^2 n2m2条边也不可能都用了,大大达不到上界时间复杂度。

代码:

#include<bits/stdc++.h>
using namespace std;
const int NN=354,dx[8]={1,-1,0,0,1,-1,1,-1},dy[8]={0,0,1,-1,1,1,-1,-1};
int n,m,c1,c2,d,sta[NN][NN],num[NN][NN];
bool can[NN][NN],vis[NN][NN][20][20];
struct node
{
    int x,y,d,c1,c2;
}q[30000004],ans=(node){0,0,1e9,1e9,1e9};
void cover(int x,int y,int k)
{
    for(int i=0;i<k;i++)
	{
	    num[max(1,x-i)][max(1,y-k+i+1)]++;
	    num[max(1,x-i)][min(m,y+k-i-1)+1]--;
	    num[min(n,x+i)][max(1,y-k+i+1)]++;
	    num[min(n,x+i)][min(m,y+k-i-1)+1]--;
	}
}
bool check(node x,node y)
{
    if(x.c1+x.c2!=y.c1+y.c2)
		return x.c1+y.c2<y.c1+y.c2;
    return x.c1<y.c1;
}
void bfs(pair<int,int>st,pair<int,int>ed)
{
    int h=1,t=0;
	q[++t]=(node){st.first,st.second};
    while(h<=t)
	{
	    node u=q[h++];
	    if(u.d>=ans.d)
		    break;
	    for(int i=0;i<8;i++)
		{
		    int nx=u.x+dx[i],ny=u.y+dy[i];
		    if(nx<1||ny<1||nx>n||ny>m||sta[nx][ny])
				continue;
		    if(!can[nx][ny])
			{
			    if(vis[nx][ny][u.c1][u.c2])
					continue;
			    vis[nx][ny][u.c1][u.c2]=true;
			    q[++t]=(node){nx,ny,u.d+1,u.c1,u.c2};
			    if(nx==ed.first&&ny==ed.second&&check(q[t],ans))
					ans=q[t];
			}
		    else
			{
			    if(u.c1>=c1||vis[nx][ny][u.c1+1][u.c2])
					continue;
			    vis[nx][ny][u.c1+1][u.c2]=true;
			    q[++t]=(node){nx,ny,u.d+1,u.c1+1,u.c2};
			    if(nx==ed.first&&ny==ed.second&&check(q[t],ans))
					ans=q[t];
			}
		}
	    for(int i=0;i<4;i++)
		{
		    int nx=u.x+dx[i]*d,ny=u.y+dy[i]*d;
		    if(nx<1||ny<1||nx>n||ny>m||sta[nx][ny])
				continue;
		    if(!can[nx][ny])
			{
			    if(u.c2>=c2||vis[nx][ny][u.c1][u.c2+1])
					continue;
			    vis[nx][ny][u.c1][u.c2+1]=true;
			    q[++t]=(node){nx,ny,u.d+1,u.c1,u.c2+1};
			    if(nx==ed.first&&ny==ed.second&&check(q[t],ans))
					ans=q[t];
			}
		    else
			{
			    if(u.c1>=c1||u.c2>=c2||vis[nx][ny][u.c1+1][u.c2+1])
					continue;
			    vis[nx][ny][u.c1+1][u.c2+1]=true;
			    q[++t]=(node){nx,ny,u.d+1,u.c1+1,u.c2+1};
			    if(nx==ed.first&&ny==ed.second&&check(q[t],ans))
					ans=q[t];
			}
		}
	}
}
int main()
{
    scanf("%d%d%d%d%d",&n,&m,&c1,&c2,&d);
    pair<int,int>st,ed;
    for(int i=1;i<=n;i++)
	    for(int j=1;j<=m;j++)
		{
		    char s[10];
		    scanf("%s",s);
		    if(s[0]=='S')
			{
			    st.first=i;
			    st.second=j;
			}
		    else if(s[0]=='T')
			{
				ed.first=i;
				ed.second=j;
			}
		    else if(s[0]!='.')
			{
				for(int k=0;s[k];k++)
					sta[i][j]=(sta[i][j]<<3)+(sta[i][j]<<1)+(s[k]-'0');
			    cover(i,j,sta[i][j]);
			}
		}
    for(int i=1;i<=n;i++)
	{
	    int sum=0;
	    for(int j=1;j<=m;j++)
		{
		    sum+=num[i][j];
		    if(sum>0)
				can[i][j]=true;
		}
	}
    bfs(st,ed);
    if(!ans.x)
		printf("-1");
    else
		printf("%d %d %d",ans.d,ans.c1,ans.c2);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值