noip2017棋盘(超级详细)

发布于:2022-08-07 ⋅ 阅读:(538) ⋅ 点赞:(0)

目录

            题干

                归纳条件

分析

 1.设变量部分

     2.输入输出部分

         3.核心代码dfs部分 

      完整AC代码

  总结


题干

  noip2017棋盘 
试题描述
 有一个m × m的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。 
 任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的) ,你只能向上、下、左、右四个方向前进。当你从一个格子走向另一个格子时,如果两个格子的颜色相同,那你不需要花费金币;如果不同,则你需要花费1个金币。 
 另外,你可以花费 2个金币施展魔法让下一个无色格子暂时变为你指定的颜色。但这个魔法不能连续使用,而且这个魔法的持续时间很短,也就是说,如果你使用了这个魔法,走到了这个暂时有颜色的格子上,你就不能继续使用魔法;只有当你离开这个位置,走到一个本来就有颜色的格子上的时候,你才能继续使用这个魔法,而当你离开了这个位置(施展魔法使得变为有颜色的格子)时,这个格子恢复为无色。 
 现在你要从棋盘的最左上角,走到棋盘的最右下角,求花费的最少金币是多少? 
输入格式
  数据的第一行包含两个正整数 m,n,以一个空格分开,分别代表棋盘的大小,棋盘上有颜色的格子的数量。 
  接下来的 n 行,每行三个正整数 x,y,c,分别表示坐标为(x,y)的格子有颜色 c。
其中 c=1 代表黄色,c=0 代表红色。相邻两个数之间用一个空格隔开。棋盘左上角的坐标为(1, 1),右下角的坐标为(m, m)。 
  棋盘上其余的格子都是无色。保证棋盘的左上角,也就是(1,1)一定是有颜色的。 
输出格式
  输出一行,一个整数,表示花费的金币的最小值,如果无法到达,输出-1。 
输入样例1
【样例输入1】
5 7 
1 1 0 
1 2 0 
2 2 1 
3 3 1 
3 4 0 
4 4 1 
5 5 0

【样例输入2】
5 5 
1 1 0 
1 2 0 
2 2 1 
3 3 1 
5 5 0
输出样例1
【样例输出1】
8

【样例输出2】
-1
注释说明
【输入输出样例1 说明】 


从(1,1)开始,走到(1,2)不花费金币 
从(1,2)向下走到(2,2)花费1枚金币 
从(2,2)施展魔法,将(2,3)变为黄色,花费2 枚金币 
从(2,2)走到(2,3)不花费金币 
从(2,3)走到(3,3)不花费金币 
从(3,3)走到(3,4)花费1枚金币 
从(3,4)走到(4,4)花费1枚金币 
从(4,4)施展魔法,将(4,5)变为黄色,花费2 枚金币,
从(4,4)走到(4,5)不花费金币 
从(4,5)走到(5,5)花费1枚金币 
共花费8枚金币。 

【输入输出样例2 说明】

从(1,1)走到(1,2),不花费金币 
从(1,2)走到(2,2),花费1金币 
施展魔法将(2,3)变为黄色,并从(2,2)走到(2,3)花费2金币 
从(2,3)走到(3,3)不花费金币 
从(3,3)只能施展魔法到达(3,2),(2,3),(3,4),(4,3) 
而从以上四点均无法到达(5,5),故无法到达终点,输出-1 

【数据规模与约定】 
对于30%的数据,1 ≤  m  ≤  5,  1 ≤  n  ≤  10。 
对于60%的数据,1 ≤  m  ≤  20,  1  ≤  n ≤  200。 
对于100%的数据,1 ≤  m  ≤  100,  1  ≤  n ≤  1,000。 
 

 

首先归纳条件

1.格子有三种情况,,无色

2.上下左右四个方向

3.只能够站在有颜色的地方

4.移动到另一格子时,若同色,则免费,若异色,则1金币,若无色,2金币

5.这道题信息量十分的大

我们从头到尾开始

分析

首先是

设变量部分

#include<bits/stdc++.h>
using namespace std;
int a[105][105],b[105][105],m,n,c,t=0,ans=1e9;
int dx[4]={1,-1,0,0},dy[4]={0,0,-1,1};

解释一下各个变量 

1.a后来是储存颜色的

2.b不过是储存答案的棋子罢了

3.m,n,c没什么好说的

4.t是记录是否找到,在最后输出的时候有用

5.dx,dy数组是方向,很常规

接下来应该是dfs 部分,但那时核心代码,最后讲

所以

先说

输入输出部分

 cin>>n>>m;
 for(int i=1;i<=n;i++){
 for(int j=1;j<=n;j++){
 b[i][j]=1e9;}}//设个贼大的值,方便求min值
 for(int i=0;i<m;i++){
  int x,y,c;
 cin>>x>>y>>c;
 if(c==0) a[x][y]=2;//红的换成2
 else a[x][y]=1;//黄的还是1
}
 dfs(1,1,0,0);
 if(t==1) cout<<ans<<endl;//找到了    
 else cout<<-1<<endl;//没找到
 }

再解释解释注释没说到的地方

1.直接设x,y,c,不然设数组的话内存又大又麻烦

2. dfs(1,1,0,0) 其中1,1表示初始坐标,第一个0存答案,第二个0表示上次没有使用魔法

最后就是

核心代码dfs部分

void dfs(int x,int y,int cnt,int k){
 if(cnt>=b[x][y]){
  return ;}//小于最小值,回去重新投胎
  b[x][y]=cnt;//投胎成功,更新最小值
  if(x==n && y==n){
  t=1;//到啦,记录一下
  ans=min(ans,cnt);//最小值
  return;
  }
  for(int i=0;i<=3;i++){//四个方向
   int ux=x+dx[i],uy=y+dy[i];
   if(ux<1 || uy<1 || ux>m || uy>m){
   continue;}//边界
   if(a[ux][uy]!=0){//下一个有颜色
   if(a[ux][uy]==a[x][y]) dfs(ux,uy,cnt,0);//同色不花钱,不用魔法
    else dfs(ux,uy,cnt+1,0);//留下买路财1金币
    }
    else {//无色
    if(k==0){//上次没用
    a[ux][uy]=a[x][y];// 魔法的力量
    dfs(ux,uy,cnt+2,1);//两金币,用魔法
    a[ux][uy]=0; 又变回来了}}}}

解释一下一些之前输入输出的设计 

1,为什么红色要换成2储存,看代码应该显而易见了(因为0储存无色);

2.dfs(1,1,0,0)应该可以理解了

ux uy真是好东西

储存方向,实在是太舒服啦!!

现在奉上完整AC代码 

完整AC代码

#include<bits/stdc++.h>
using namespace std;
int a[105][105],b[105][105],m,n,c,t=0,ans=1e9;
int dx[4]={1,-1,0,0},dy[4]={0,0,-1,1};
void dfs(int x,int y,int cnt,int k){
 if(cnt>=b[x][y]){
  return ;}
  b[x][y]=cnt;
  if(x==n && y==n){
  t=1;
  ans=min(ans,cnt);
  return;
  }
  for(int i=0;i<=3;i++){
   int ux=x+dx[i],uy=y+dy[i];
   if(ux<1 || uy<1 || ux>m || uy>m){
   continue;}
   if(a[ux][uy]!=0){
   if(a[ux][uy]==a[x][y]) dfs(ux,uy,cnt,0);
    else dfs(ux,uy,cnt+1,0);
    }
    else {
    if(k==0){
    a[ux][uy]=a[x][y];
    dfs(ux,uy,cnt+2,1);
    a[ux][uy]=0;}}}}
 int main(){
 cin>>n>>m;
 for(int i=1;i<=n;i++){
 for(int j=1;j<=n;j++){
 b[i][j]=1e9;}}
 for(int i=0;i<m;i++){
  int x,y,c;
 cin>>x>>y>>c;
 if(c==0) a[x][y]=2;
 else a[x][y]=1;
}
 dfs(1,1,0,0);
 if(t==1) cout<<ans<<endl;
 else cout<<-1<<endl;
 }

总结

dfs yyds

谢谢,欢迎讨论!

 

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到