首页 > 其他分享 >「NOIP2017 普及组」棋盘 题解

「NOIP2017 普及组」棋盘 题解

时间:2023-08-21 21:46:19浏览次数:56  
标签:NOIP2017 1145 int 题解 dfs nx ny mp 棋盘

前言

一个绿题,风光啊 QwQ

题面

传送门

思路

怎么走

我们定义一个函数 dfs(x,y,coin,can,color)

x,y表示坐标,coin表示当前的金币数量,color表示当前坐标的颜色,can表示当前是否能施展魔法。

再加一个mp数组记录颜色,-1表示无颜色,0表示红色,1表示黄色

为什么不直接使用 mp[x][y] 获取颜色呢?从题目中可知,如果下一个格子没有颜色,需要施展“魔法”改变颜色才可以移动,而且这个魔法是临时的,离开之后就会复原,所以需要传一个颜色进去,不然会影响下一步的颜色判定。

与大部分棋盘类dfs相似,我们定义两个数组fx,fy来记录每个方向两轴的变化:const int fx[5]={0,-1,1,0,0},fy[5]={0,0,-1,1},写一个1-4的循环,每一次的nx,ny(下一个方向的坐标)就为x+fx[i],y+fy[i]

搜索就很简单了,如果mp[nx][ny]==color,那么直接搜索:

dfs(nx,ny,coin,1,color);

不然的话,如果mp[nx][ny]!=-1(即不是无颜色),搜索,并将金币+1:

dfs(nx,ny,coin+1,1,mp[nx][ny]);

否则就没有颜色了,施展魔法将其变为当前格子颜色,因为魔法只能施展一次,前面的can变量就起效了,如果上一次施展过魔法,can为0,就不可以走这个格子了,然后金币+2,即mp[nx][ny]==-1&&can&&coin+2<minmp[nx][ny]

dfs(nx,ny,coin+2,0,color);

接下来我们需要一些边界条件,如果到达目标点,即为x==m&&y==m时,退出循环并记录最小值。如果x和y超过合法范围也要返回:

if(x>m||y>m||x<1||y<1) return;
if(x==m&&y==m){
    minc=min(minc,coin);
    return;
}

除此之外,你需要一个vis数组记录当前走过的部分(回溯!!!),不然你会得到壮观的MLE:

这就是简单的搜索逻辑。

剪枝

作为一个普及组的T3,哪有那么容易让你拿分呢?

终于,你写出了代码,你披星戴月、奋不顾身,只为证明——只因它太暴力:

#include <bits/stdc++.h>
using namespace std;
const int inf=0x7fffffff,fx[5]={0,1,-1,0,0},fy[5]={0,0,0,1,-1};
int m,n,mp[1145][1145],minc=inf,nx,ny,vis[1145][1145];
void dfs(int x,int y,int coin,bool can,int color){
	if(coin>minc) return;
	if(vis[x][y]) return;
    if(x>m||y>m||x<1||y<1) return;
	if(x==m&&y==m){
        // cout<<"c:"<<coin<<endl<<endl;
		minc=min(minc,coin);
		return;
	}
    // cout<<x<<" "<<y<<" "<<coin<<" "<<can<<endl;
	vis[x][y]=1;
	for(int i=1;i<=4;i++){
		nx=x+fx[i],ny=y+fy[i];
		if(color==mp[nx][ny]){
			dfs(nx,ny,coin,1,color);
		}
		else if(mp[nx][ny]!=-1){
			dfs(nx,ny,coin+1,1,mp[nx][ny]);
		}
		else if(mp[nx][ny]==-1&&can){
			dfs(nx,ny,coin+2,0,color);
		}
	}
	vis[x][y]=0;
}
int main(){
	ios::sync_with_stdio(0);
	memset(mp,-1,sizeof(mp));
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		int x,y,c;
		cin>>x>>y>>c;
		mp[x][y]=c;
	}
	dfs(1,1,0,1,mp[1][1]); 
	if(minc==inf) cout<<-1;
	else cout<<minc;
	return 0;
}

显然,这需要剪枝。

对于剪枝来说,有这几种思路:

  1. 发现当前金币已经比最小的多了,就不用搜下去了
  2. 在循环中就可以判断搜索合法性了,减小函数调用开销(PS:我习惯把这玩意写在边界条件上面,看起来差不多,实则增加了函数调用开销,调用后才返回,花费显然很大
  3. 记忆化,记录走到当前坐标合法路径的最小金币,如果超过就可以不搜了,比第一个更高级

关于记忆化,我们可以定义一个数组minmp[1145][1145]并初始化为inf(用memset需要改为127)

然后在循环里面判断即可。

代码

终于可以上代码了:

#include <bits/stdc++.h>
using namespace std;
const int inf=0x7fffffff,fx[5]={0,1,-1,0,0},fy[5]={0,0,0,1,-1}; //每个方向xy变化
int m,n,mp[1145][1145],minc=inf,nx,ny,vis[1145][1145],minmp[1145][1145]; //mp记录颜色,minc为最小金币,vis用于回溯。minmp为某个当前最小值
void dfs(int x,int y,int coin,bool can,int color){ //定义见上
	if(x==m&&y==m){ //边界
        // cout<<"c:"<<coin<<endl<<endl;
		minc=min(minc,coin);
		return;
	}
	minmp[x][y]=coin;
    // cout<<x<<" "<<y<<" "<<coin<<" "<<can<<endl;
	vis[x][y]=1; //回溯
	for(int i=1;i<=4;i++){
		nx=x+fx[i],ny=y+fy[i];
		if(nx>m||ny>n||nx<1||ny<1||vis[nx][ny]) continue; //不成立条件写循环里,减小开销
		if(color==mp[nx][ny]&&coin<minmp[nx][ny]) //相同颜色
			dfs(nx,ny,coin,1,color);
		else if(mp[nx][ny]!=-1&&coin+1<minmp[nx][ny]) //不同颜色
			dfs(nx,ny,coin+1,1,mp[nx][ny]);
		else if(mp[nx][ny]==-1&&can&&coin+2<minmp[nx][ny]) //没有颜色,施展魔法
			dfs(nx,ny,coin+2,0,color);
	}
    //走完了
	vis[x][y]=0;
}
int main(){
	ios::sync_with_stdio(0);
	memset(mp,-1,sizeof(mp)); //初始化
    memset(minmp,127,sizeof(minmp));
	cin>>m>>n;
	for(int i=1;i<=n;i++){ //输入数据
		int x,y,c;
		cin>>x>>y>>c;
		mp[x][y]=c;
	}
	dfs(1,1,0,1,mp[1][1]); 
	if(minc==inf) cout<<-1; //没搜到
	else cout<<minc;
	return 0;
}

标签:NOIP2017,1145,int,题解,dfs,nx,ny,mp,棋盘
From: https://www.cnblogs.com/LYXOfficial/p/17647154.html

相关文章

  • [CF1790F] Timofey and Black-White Tree 题解
    [CF1790F]TimofeyandBlack-WhiteTree题解题目描述ZYH有一棵\(n\)个节点的树,最初\(c_0\)号节点是黑色,其余均为白色。给定操作序列\(c_1,c_2,\cdots,c_{n-1}\),第\(i\)次操作表示将\(c_i\)号节点染黑。每次操作后,输出距离最近的两个黑点间的距离。两点\(u,v\)间......
  • CF1762E Tree Sum 题解
    题意对于一棵\(n\)个节点的树\(T\),定义\(\operatorname{good}(T)\)为真当且仅当边权\(w\in\left\{-1,1\right\}\)且对于任意节点\(u\),均有\(\displaystylef(u)=\prod\limits_{\left(u,v\right)\inE}w\left(u,v\right)=-1\)。求\[\sum\limits_{\operat......
  • RTSP/Onvif视频服务器EasyNVR安防视频云服务调用接口录像会被自动删除的问题解决方案
    EasyNVR安防视频云服务是基于RTSP/Onvif协议接入的视频平台,可支持将接入的视频流进行全平台、全终端的分发,分发的视频流包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等。平台丰富灵活的视频能力,可应用在智慧校园、智慧工厂、智慧水利等场景中。有用户反馈,在使用EasyNVR接入设备......
  • [ABC315G] Ai + Bj + Ck = X (1 <= i, j, k <= N) 题解
    [ABC315G]Ai+Bj+Ck=X(1<=i,j,k<=N)题解题目描述求题目中式子的数量。思路因为\(N\le10^6\),所以考虑枚举\(k\),那么变为求\(ai+bj=x-ck,i,j\in[1,N]\),这个问题可以通过Exgcd算法求解。首先考虑求出一组\(i,j\)的特解\(x',y'\),根据通解\(x=x'+......
  • Codeforces Round 893 (Div. 2) A-C题解
    CF893(Div.2)A.Buttons签到题。两人会优先选择c中的按钮来,避免自己的按钮消耗同时减少对方可选择的按钮。所以c%2==1等价于a的按钮数+1,c%2==0时相当于c按钮不存在,比较ab按钮的数量来得出答案即可。#include<iostream>usingnamespacestd;typedeflonglong......
  • [AGC005C] Tree Restoring 题解
    比较简单的题。思路我们可以把一棵树抽象成一条极长的链上挂了很多的点。观察这样的树的性质。除去中间的每一个\(dis\)至少有两个点的\(a_i=dis\)。考虑这条链的长度为\(s\)。那么对于中间的点,我们可以分两种情况讨论。\(s\)为偶数那么我们必然要求在中间的权值只......
  • hive sql运行时候reduce 只有2个问题解决
    我们在explansql时候发现width是负数,事实上原因width是通过dataSize/rowNum计算出来的,这两个参数都是在执行计划中根据每个operator通过stats计算出来的。对于selectquery来说,datasize是根据columnstats、尤其是non-null的数据计算出来的,这些non-nullvalue按照如下公......
  • RTSP/Onvif流媒体服务器EasyNVR安防视频平台一直提示网络请求失败的问题解决方案
    EasyNVR平台优秀的视频能力在于通过RTSP/ONVIF协议,将前端接入设备的音视频资源进行采集,并转码成适合全平台、全终端分发的视频流格式,包括RTMP、RTSP、FLV、HLS、WebRTC等格式。有用户反馈,EasyNVR使用过程中,突然提示网络请求失败,视频也无法播放,请求我们协助排查。此前我......
  • 安防视频监控平台EasyCVR视频集中存储平台接入RTSP设备出现离线情况的问题解决方案
    安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力,也具备接入AI智能分析的能......
  • UVA1589 象棋 题解
    0.题目大意在一个\(10\times9\)的网格上,可以游玩象棋。在本题中,我们考虑如下几个简化的规则:每一个棋子下在交点上,一个交点不能同时有两个棋子;棋盘的左上角为\((1,1)\),右下角为\((10,9)\);当一个棋子移动到它的敌人的棋子上,就说敌方的棋子要被“吃掉”。当棋盘上的“将”有......