题目

题目描述

有一个m × m的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。

任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的),你只能向上、下、左、右四个方向前进。当你从一个格子走向另一个格子时,如果两个格子的颜色相同,那你不需要花费金币;如果不同,则你需要花费1 个金币。

另外,你可以花费2 个金币施展魔法让下一个无色格子暂时变为你指定的颜色。但这个魔法不能连续使用,而且这个魔法的持续时间很短,也就是说,如果你使用了这个魔法,走到了这个暂时有颜色的格子上,你就不能继续使用魔法;只有当你离开这个位置,走到一个本来就有颜色的格子上的时候,你才能继续使用这个魔法,而当你离开了这个位置(施展魔法使得变为有颜色的格子)时,这个格子恢复为无色。

现在你要从棋盘的最左上角,走到棋盘的最右下角,求花费的最少金币是多少?

输入

第一行包含两个正整数m,n,以一个空格分开,分别代表棋盘的大小,棋盘上

有颜色的格子的数量。

接下来的 n 行,每行三个正整数x,y,c,分别表示坐标为(x,y)的格子有颜色c。

其中c=1 代表黄色,c=0 代表红色。相邻两个数之间用一个空格隔开。棋盘左上角的坐标

为(1, 1),右下角的坐标为(m, m)。

棋盘上其余的格子都是无色。保证棋盘的左上角,也就是(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

样例输出

8

输入输出样例 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。

题解

个人感觉这道题放在T3还是比较合理的(然而T3与今年特水的T1,T2相比还是落差太大了233…)。这道题实际上并不难,然而很多人都把它想复杂了(我这种蒟蒻都能在考场上想10分钟想出来的题233)

首先做题的时候要注意数据范围,m仅仅只有1~100很显然可能是3重循环或者记忆化搜索,再结合题目,可以得出本题大概是最短路,然而一群dalao选择了使用SPFA….实际上这题只需要DFS/BFS+剪枝(即为A*)就可以了。相信大家都会打DFS/BFS的代码在这里就不介绍解法了。

接下来就是剪枝了….我们可以这样想,当一个棋子走到(x,y)这个点的时候,如果比之前走到(x,y)这个点所产生的钱数更大,我们是可以将这个节点直接扔掉的。这点是可以证明的。另外我们也要额外考虑施展魔法变色的时所变空白格子的颜色——这里可以用贪心的思想,很容易证明不管在什么情况下,我们总需要保持变为的格子与上一个格子颜色相同。

至此本问题已经基本上解决了,只需要打个暴力再优化一下就可以了…

实现

//dfs
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<climits>
using namespace std;
int n,m,f[105][105];
int map[105][105];
int sx[4]={-1,0,1,0},sy[4]={0,-1,0,1};
void dfs(int x,int y,int money,int color,bool mai)
{
    if(money>=f[x][y])return ;
    f[x][y]=money;
    for(int i=0;i<=3;i++)
    {
        if(map[x+sx[i]][y+sy[i]]==0)
        {if(!mai)dfs(x+sx[i],y+sy[i],money+2,color,1);continue;}
        else
        {
        if(map[x+sx[i]][y+sy[i]]==color)dfs(x+sx[i],y+sy[i],money,map[x+sx[i]][y+sy[i]],0);
        if(map[x+sx[i]][y+sy[i]]!=color)dfs(x+sx[i],y+sy[i],money+1,map[x+sx[i]][y+sy[i]],0);
    }
    }
}
int main()
{
    //freopen("chess.in","r",stdin);
    //freopen("chess.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            f[i][j]=INT_MAX;
    for(int i=1;i<=m;i++)
    {
        int x,y,color;
        scanf("%d%d%d",&x,&y,&color);
        map[x][y]=color+1;
    }
    dfs(1,1,0,map[1][1],0);
    if(f[n][n]==INT_MAX){printf("-1\n");return 0;}
    else printf("%d\n",f[n][n]);
    return 0;
}
//转自TLY的BFS代码
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int MAXN = 100;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
int a[MAXN+5][MAXN+5];
int MIN[MAXN+5][MAXN+5][2];
struct NODE{
    int x,y,t,c;
}p;
bool operator<(NODE a,NODE b)
{
    return a.t>b.t;
}
priority_queue<NODE>que;
int main()
{
    //freopen("chess.in","r",stdin);
    //freopen("chess.out","w",stdout);
    memset(a,-1,sizeof(a));
    memset(MIN,63,sizeof(MIN));
    int m,n;
    scanf("%d %d",&m,&n);
    for(int i=1;i<=n;i++)
    {
        int x,y,c;
        scanf("%d %d %d",&x,&y,&c);
        a[x][y] = c;
    }
    p.x = 1;p.y = 1;
    p.t = 0;p.c = a[1][1];
    MIN[1][1][a[1][1]] = 0;
    que.push(p);
    do
    {
        p = que.top();que.pop();
        if( p.x == m && p.y == m )
        {
            printf("%d\n",p.t);
            return 0;
        }
        if( p.t < MIN[p.x][p.y][p.c] ) continue;
        for(int i=0;i<4;i++)
        {
            NODE q;
            q.x = p.x + dir[i][0];
            q.y = p.y + dir[i][1];
            if( q.x < 1 || q.y < 1 || q.x > m || q.y > m ) continue;
            if( a[q.x][q.y] == -1 )
            {
                if( a[p.x][p.y] == -1 ) continue;
                q.t = p.t + 2;
                q.c = p.c;
                if( q.t < MIN[q.x][q.y][q.c] )
                {
                    MIN[q.x][q.y][q.c] = q.t;
                    que.push(q);
                }
                q.c = !(p.c);
                q.t++;
                if( q.t < MIN[q.x][q.y][q.c] )
                {
                    MIN[q.x][q.y][q.c] = q.t;
                    que.push(q);
                }
            }
            else
            {
                q.t = p.t;q.c = a[q.x][q.y];
                if( q.c != p.c ) q.t++;
                if( q.t < MIN[q.x][q.y][q.c] )
                {
                    MIN[q.x][q.y][q.c] = q.t;
                    que.push(q);
                }
            }
        }
    }while(!que.empty());
    printf("-1\n");
    return 0;
}
//某初三dalao的SPFA
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define maxn 105
#define maxe 2000005
using namespace std;

int n,m,col[maxn][maxn],cnt,mark[maxn][maxn],dis[10005],
dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};

bool inq[10005];

int info[maxe],Prev[maxe],to[maxe],cst[maxe],cnt_e;

void insert(int u,int v,int w){
    cnt_e++;
    Prev[cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=w;
}

bool check(int u,int v){
    return u>=1 && u<=m && v>=1 && v<=m && col[u][v]!=-1;
}

int main(){
    int u,v,c,x,y;
    scanf("%d%d",&m,&n);
    memset(col,-1,sizeof col);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&u,&v,&c);
        col[u][v]=c;
        mark[u][v]=++cnt;
    }

    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if(col[i][j]!=-1)
                for(int k=0;k<4;k++)
                {
                    if(!check(u=i+dir[k][0],v=j+dir[k][1])) continue;
                    insert(mark[i][j],mark[u][v],col[i][j]^col[u][v]);
                    insert(mark[u][v],mark[i][j],col[i][j]^col[u][v]);
                }
            else{
                for(int k=0;k<4;k++)
                    for(int l=k+1;l<4;l++)
                        if(check(u=i+dir[k][0],v=j+dir[k][1]) && check(x=i+dir[l][0],y=j+dir[l][1]))
                        {
                            insert(mark[x][y],mark[u][v],(col[x][y]^col[u][v])+2);
                            insert(mark[u][v],mark[x][y],(col[x][y]^col[u][v])+2);
                        }
            }
        }

    queue<int>q;
    q.push(mark[1][1]);
    memset(dis,0x3f,sizeof dis);
    dis[mark[1][1]]=0;inq[mark[1][1]]=1;
    int now;
    while(!q.empty()){
        now=q.front();q.pop();inq[now]=0;
        for(int i=info[now];i;i=Prev[i])
            if(dis[to[i]]>dis[now]+cst[i])
            {
                dis[to[i]]=dis[now]+cst[i];
                if(!inq[to[i]])
                {
                    inq[to[i]]=1;
                    q.push(to[i]);
                }
            }
    }

    if(dis[mark[m][m]]==0x3f3f3f3f) dis[mark[m][m]]=-1;
    if(mark[m][m]) printf("%d",dis[mark[m][m]]);
    else{
        int ans=0x3f3f3f3f;
        for(int i=0;i<4;i++)
            if(check(u=m+dir[i][0],v=m+dir[i][1]))
                ans=min(ans,dis[mark[u][v]]+2);
        if(ans==0x3f3f3f3f) ans=-1;
        printf("%d",ans);
    }
}

[NOIP2017普及组]棋盘的更多相关文章

  1. P3956 [NOIP2017 普及组] 棋盘

    P3956 [NOIP2017 普及组] 棋盘 题目 题目描述 有一个 m×m 的棋盘,棋盘上每一个格子可能是红色.黄色或没有任何颜色的.你现在要从棋盘的最左上角走到棋盘的最右下角. 任何一个时刻,你 ...

  2. [NOIP2017普及组]跳房子(二分,单调队列优化dp)

    [NOIP2017普及组]跳房子 题目描述 跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一. 跳房子的游戏规则如下: 在地面上确定一个起点,然后在起点右侧画 nn 个格子, ...

  3. 「LOJ 6373」NOIP2017 普及组题目大融合

    NOIP2017 普及组题目大融合 每个读者需要有某个后缀的书,可以暴力map,复杂度\(o(9*nlog(n))\),也可以反串建trie树,复杂度\(o(9*n)\). 故可以求出需要的最少的RM ...

  4. noip2017普及组

    过了这么久才来写博客,也是我这么一段时间都很低迷吧.... 老实来说,今年应该是要打提高组的...可还是打了普及组... 其实最猥琐的还是我连普及都写挂了,作为一个学了两年的人,图论,进阶dp都写过的 ...

  5. NOIP2017普及组比赛总结

    期中考总结&NOIP2017总结 2017年11月11日,我第二次参加NOIP普及组复赛.上一年,我的得分是250分,只拿到了二等奖.我便把目标定为拿到一等奖,考到300分以上. 早上8点多, ...

  6. NOIP2017普及组解题报告

    刚参加完NOIP2017普及,只考了210,于是心生不爽,写下了这篇解题报告...(逃 第一次写博,望dalao们多多指导啊(膜 第一题score,学完helloworld的人也应该都会吧,之前好多人 ...

  7. NOIP2017普及组初赛试题及答案

    普及组C++语言试题 一.单项选择题(共 20 题,每题 1.5 分,共计 30 分:每题有且仅有一个正确选项) 1.在 8 位二进制补码中,10101011 表示的数是十进制下的( ). A. 43 ...

  8. NOIP2017普及组T2题解

    还是神奇的链接 上面依然是题目. 这道题依然很简单,比起2015年的普及组t2好像还是更水一些. 不过这道题能讲的比第一题多. 我们一起来看一下吧! 这一题,我们首先将书的编号全部读入,存在一个数组里 ...

  9. NOIP2017普及组初赛总结

    去年,我普及组复赛翻车,居然没进一等奖,于是,今年,我只能再做一次普及组. 这次初赛我93.5分,居然是中山市第一--(中山市太弱了?) 其实我觉得我没考好. 比赛时第二题(计算机存储数据的基本单位是 ...

随机推荐

  1. 前端性能优化 —— 添加Expires头

    简要:添加Expires头能有效的利用浏览器的缓存能力来改善页面的性能,能在后续的页面中有效避免很多不必要的Http请求,WEB服务器使用Expires头来告诉Web客户端它可以使用一个组件的当前副本 ...

  2. (77)Wangdao.com第十五天_JavaScript 用于数据交换的文本格式 JSON 对象

    JSON 对象 JSON (JavaScript Object Notation 的缩写) 也是一种数据,是 JavaScript 的原生对象,用来处理 JSON 格式数据.它有两个静态方法:JSON ...

  3. react_app 项目开发

    react_app 项目开发 npm install -g create-react-app npm root -g        // 查看安装包位置 创建项目 create-react-app m ...

  4. centos7-- sbt的安装使用

    sbt是一款类似于maven的构建工具 安装sbt curl https://bintray.com/sbt/rpm/rpm > bintray-sbt-rpm.repo mv bintray- ...

  5. [LeetCode] Generate Random Point in a Circle 生成圆中的随机点

    Given the radius and x-y positions of the center of a circle, write a function randPoint which gener ...

  6. Java 中关于基本数字类型的注意事项

    局部变量需初始化才能访问 public void test() { float n; n = n + 1; } 窄化导致自增异常 short i = 3; i += 1; // 不提升 short i ...

  7. oracle统计数据时,涉及两个表的数据

    SELECT t1.*,a.num FROM (SELECT SUM(t.total_profit) total_profit, SUM(t.main_business_income) main_bu ...

  8. sqlserver 常见的表名修改

    查看表:exec sp_help 表名 查看列: exec sp_columns 表名 查看列:select * from information_schema.columns where table ...

  9. 3. Scala运算符

    3.1 运算符介绍 运算符是一种特殊的符号,用以表示数据的运算.赋值和比较等 1) 算术运算符 2) 赋值运算符 3) 比较运算符(关系运算符) 4) 逻辑运算符 5) 位运算符 3.2 算术运算符 ...

  10. Mac上配置GTK环境

    Mac上配置GTK环境 安装command line工具, 如果安装了Xcode, 就直接跳过该步骤 安装Homebrew 使用brew install pkg-config 使用brew insta ...