1 solutions

  • 1
    @ 2025-10-9 13:21:21

    题意

    荆轲刺秦王

    在方格地图内两点之间寻找最优路线(8宫格+技能)

    优先选择时间最少的路线,其次选择总技能最少的,再选择隐身最少的,最后是瞬移最少的

    注意,瞬移是只有4宫格的方向,普通移动是8宫格的

    思路

    通过方向数组记录位移偏差,不然每个方向都写就很难调代码了

    这里可以只用一个方向数组,前4个是4宫格的,后四个是8宫格的

    代码

    #include<bits/stdc++.h>
    #define pii pair<int,int>//位置
    #define N 355
    #define INF 0x3f3f3f3f
    using namespace std;
    int n,m,c1,c2,d;
    int g[N][N];
    int cf[N][N];
    int xl[8]={0,0,1,-1,1,-1,1,-1};
    int yl[8]={1,-1,0,0,1,-1,-1,1};
    pii s,t;
    bool check_e(int i,int j)//判断是否越界
    {
    	return i<1 || j<1 || i>n || j>m;
    }
    void fun(int x,int y,int d)//通过差分标记士兵的观察范围
    {
    	for(int i=0;i<=d;i++)//每一行
    	{
    		cf[max(1,x-i)][max(1,y-(d-i))]++;//左上
    		cf[max(1,x-i)][min(y+(d-i),m)+1]--;//右上
    		cf[min(x+i,n)][max(1,y-(d-i))]++;//左下
    		cf[min(x+i,n)][min(y+(d-i),m)+1]--;//右下
    	}
    }
    struct node
    {
    	pii p;//位置
    	int st,c1,c2;//时间,剩下隐身的次数,剩下瞬移的次数
    	bool operator < (const node nxt)const//比较哪个更优秀
    	{
    		if(st<nxt.st)
    			return 1;
    		else if(st==nxt.st)//时间
    		{
    			if(c1+c2>nxt.c1+nxt.c2)//总技能,剩下的技能越多的越好!!!
    				return 1;
    			else if(c1+c2==nxt.c1+nxt.c2)
    			{
    				if(c1>nxt.c1)//隐身,剩下的技能越多的越好!!!
    					return 1;
    				else if(c1==nxt.c1)
    				{
    					if(c2>nxt.c2)//瞬移,剩下的技能越多的越好!!!
    						return 1;
    				}
    			}
    		}
    		return 0;
    	}
    };
    queue<node>q;
    bool vis[N][N][20][20];//需要4种状态记录
    node bfs()
    {
    	q.push({s,0,c1,c2});//这里记录的是剩下的次数!!!不要搞错了
    	node b={s,INF,c1,c2};//初始化
    	while(!q.empty())
    	{
    		node e=q.front();q.pop();
    		int x=e.p.first,y=e.p.second;
    		if(e.st>b.st)continue;//择优剪枝
    		if(vis[x][y][e.c1][e.c2])continue;
    		vis[x][y][e.c1][e.c2]=1;
    		if(pii{x,y}==t)
    		{
    			if(e<b)b=e;//是否更优秀
    			continue;
    		}
    		for(int i=0;i<8;i++)//没有使用瞬移,8宫格
    		{
    			int xs=x+xl[i],ys=y+yl[i];
    			if(check_e(xs,ys))continue;
    			if(g[xs][ys]==2)continue;//这里是士兵,注意,不是观察范围,隐身也走不了
    			if(!g[xs][ys])//不使用隐身
    				q.push({{xs,ys},e.st+1,e.c1,e.c2});
    			else if(e.c1)//使用隐身
    				q.push({{xs,ys},e.st+1,e.c1-1,e.c2});//是减次数!!!
    		}
    		if(e.c2)//使用瞬移
    		{
    			for(int i=0;i<4;i++)//4宫格
    			{
    				int xs=x+d*xl[i],ys=y+d*yl[i];//记得有长度,需要 *d
    				if(check_e(xs,ys))continue;
    				if(g[xs][ys]==2)continue;//这里是士兵,注意,不是观察范围,隐身也走不了
    				if(!g[xs][ys])//不使用隐身
    					q.push({{xs,ys},e.st+1,e.c1,e.c2-1});//是减次数!!!
    				else if(e.c1)//使用隐身
    					q.push({{xs,ys},e.st+1,e.c1-1,e.c2-1});//是减次数!!!
    			}
    		}
    	}
    	return b;
    }
    int main()
    {
    	cin>>n>>m>>c1>>c2>>d;
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=m;j++)
    		{
    			string o;cin>>o;//选择string进行输入
    			if(o==".")continue;
    			else if(o=="S")s={i,j};
    			else if(o=="T")t={i,j};
    			else fun(i,j,stoi(o)-1),g[i][j]=2;//士兵,记得fun的距离要减1
    		}
    	}
    	for(int i=1;i<=n;i++)//通过差分数组求出原来的观察范围
    	{
    		int sum=0;
    		for(int j=1;j<=m;j++)
    		{
    			sum+=cf[i][j];
    			if(sum>0 && !g[i][j])//不要把士兵位置给标记了,会出错的
    				g[i][j]=1;//观察范围是1,士兵是2
    		}
    	}
    	node f=bfs();
    	if(f.st!=INF)cout<<f.st<<" "<<c1-f.c1<<" "<<c2-f.c2;//我记录的是剩下的次数,所以需要用总次数减去
    	else cout<<-1;
    	return 0;
    }
    
    • 1

    Information

    ID
    5503
    Time
    3000ms
    Memory
    512MiB
    Difficulty
    5
    Tags
    # Submissions
    25
    Accepted
    1
    Uploaded By