题目描述

线现在在一个地图上,它正在(1,1)上(左上角),最终要去到(M,N)上。它不但只能往下或往右走,还只能在整数格子上移动。
Imakf有的时候想要炫技,又有时想偷懒,所以他会告诉你这张地图的全貌,你要告诉他到达终点的最多和最少拐弯次数。

分析

一开始写这道题目的时候,比较早,所以就不会往DP的方向想。
我一开始跑了一个三元组的dijkstra,(i,j,k)表示出于(i,j)的格子,然后面向方向是k(k[0..1]),的最大拐弯数。
和最小的拐弯数,成功60分,也不知道怎么回事。WA了那一点是因为没有判断一开始的节点是不是非法的。
但是后面莫名TLE,这是为什么我都不知道。可能我成功把dijkstra变成了dfs吧(听说DFS是超时的)。
附上比赛时候自己写的代码。。。

// luogu-judger-enable-o2
# include <bits/stdc++.h>
# define Inf 0x7fffffff
# define Ri register int
# define for1(i,a,b) for (Ri i(a);i <= b; ++i)
# define for2(i,a,b) for (Ri i(a);i >= b; --i )
using namespace std;

const int dx[2] = { 1 , 0 };
const int dy[2] = { 0 , 1 };

inline int read ()
{
    int w = 0,x = 0; char ch = 0;
    while ( !isdigit(ch) ) { w |= ch == '-'; ch = getchar() ; }
    while ( isdigit(ch) ) { x = (x<<1) + (x<<3) + (ch^48); ch = getchar() ; }
    return w ? -x : x ;
}

struct node1{
    int x, y ,dis ,dir;
    friend bool operator<(node1 a,node1 b)
    {
        return a.dis<b.dis;
    }
};

struct node2{
    int x, y ,dis ,dir;
    friend bool operator<(node2 a,node2 b)
    {
        return a.dis>b.dis;
    }
};
const int Maxn = 1005;

int n , m;
int dist1[Maxn][Maxn][2], dist2[Maxn][Maxn][2];
int a[Maxn][Maxn];

inline void dijkstra1()
{
    priority_queue<node1>q;
    while (!q.empty()) q.pop();
    for1(i ,1 ,n ) for1(j ,1 ,m ) for1(k ,0 ,1 ) dist1[i][j][k] = 0;
    for1(i ,0 ,1 ) q.push((node1) {1,1,0,i});
    while (!q.empty())
    {
        node1 u=q.top();
        q.pop();
        if (u.dis < dist1[u.x][u.y][u.dir] ) continue;
        for1 (i, 0, 1)
        {
            int nx = u.x + dx[i], ny = u.y + dy[i];
            if (nx < 1 || nx > n || ny < 1 || ny > m) continue;
            if ( a[nx][ny] == 0 ) continue;
            int cc = 0;
            if (u.dir != i) cc = 1;
            if ( dist1[nx][ny][i] < u.dis + cc)
            {
                dist1[nx][ny][i] = u.dis + cc;
                q.push((node1){nx, ny, dist1[nx][ny][i], i});
            }
        }
    }
}

inline void dijkstra2()
{
    priority_queue<node2>q;
    while (!q.empty()) q.pop();
    for1(i ,1 ,n ) for1(j ,1 ,m ) for1(k ,0 ,1 ) dist2[i][j][k]=Inf;
    for1(i ,0 ,1 ) q.push((node2){1,1,0,i});
    while (!q.empty())
    {
        node2 u=q.top();
        q.pop();
        if (u.dis > dist2[u.x][u.y][u.dir] ) continue;
        for1 (i, 0, 1)
        {
            int nx = u.x + dx[i], ny = u.y + dy[i];
            if (nx < 1 || nx > n || ny < 1 || ny > m) continue;
            if ( a[nx][ny] == 0 ) continue;
            int cc = 0;
            if (u.dir != i) cc = 1;
            if ( dist2[nx][ny][i] > u.dis + cc)
            {
                dist2[nx][ny][i] = u.dis + cc;
                q.push((node2){nx, ny, dist2[nx][ny][i], i});
            }
        }
    }
}

int main ()
{
    n = read() ,m = read() ;
    for1 (i ,1 ,n )
    {
        char s[1005];
        scanf("%s",s);
        for1 (j ,0 ,m )
        {
            if (s[j]=='o') a[i][j+1] = 1;
            else a[i][j+1] = 0;
        }
    }
    dijkstra1();  dijkstra2();
    int ans1 = 0 , ans2 = Inf;
    for1 (i ,0 ,1 ) ans1 = max(ans1 , dist1[n][m][i] ),ans2 = min(ans2,  dist2[n][m][i]);
    if (ans1 == 0 && ans2 == Inf ) printf ("-1\n");
    else printf ("%d %d\n", ans1-1, ans2);
    return 0;
}

100pt

满分做法就是DP了,状态也非常好想到就是我们上面提到的三元组,将其转化成状态就可以了。
f[i][j][k]表示在(i,j)的格子上,面朝方向是k的(0..1)(0代表面朝下面,1表示面朝右边)时的最小答案。
g[i][j][k]表示在(i,j)的格子上,面朝方向是k的(0..1)(0代表面朝下面,1表示面朝右边)时的最小答案。
那么转移方程也是非常好写的。
我们先考虑(i-1,j) 转移到(i,j)那么一定是往下走,那么只能推到f[i][j][0]的状态。
那么就判断一下,如果原来的k是0的话,那么步数+1。否则就是直接推过来。
那么第二种情况就是从(i,j-1)走过来,那么一定是往右边走,那么只能推到f[i][j][1]的状态。
接下来的转移和上方一样。
不要忘记判断起点是#的情况。
而且如果不是用深搜判断,那么就不能用全用or判断,因为结尾方向可能只有一个。必须用and。

ac代码

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define N 1005
using namespace std;
char s[N];
int f[N][N][2], g[N][N][2];
int a[N][N];
int n, m;
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++) {
        scanf("%s", s);
        int len = strlen(s);
        for (int j = 0; j < len; j ++) {
            if (s[j] == 'o') a[i][j + 1] = 0;
            else a[i][j + 1] = 1;
        }
    }
    if (a[1][1]) {
        printf("-1");
        return 0;
    }
    memset(f, inf, sizeof(f));
    memset(g, -inf, sizeof(g));
    f[1][1][0] = f[1][1][1] = g[1][1][1] = g[1][1][0] = 0;
    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= n; j ++) {
            if (a[i][j]) continue;
            if (i > 1) {
                f[i][j][0] = min(f[i][j][0], f[i - 1][j][1] + 1);
                f[i][j][0] = min(f[i][j][0], f[i - 1][j][0]);
                g[i][j][0] = max(g[i][j][0], g[i - 1][j][1] + 1);
                g[i][j][0] = max(g[i][j][0], g[i - 1][j][0]);
            }
            if (j > 1) {
                f[i][j][1] = min(f[i][j][1], f[i][j - 1][0] + 1);
                f[i][j][1] = min(f[i][j][1], f[i][j - 1][1]);
                g[i][j][1] = max(g[i][j][1], g[i][j - 1][0] + 1);
                g[i][j][1] = max(g[i][j][1], g[i][j - 1][1]);
            }
        }
    }
    if ((g[n][m][1] == -inf && g[n][m][0] == -inf) || (f[n][m][1] == inf && f[n][m][0] == inf)) printf("-1\n");
    else printf("%d %d\n", max(g[n][m][1], g[n][m][0]) - 1, min(f[n][m][1], f[n][m][0]));
    return 0;
}

[luogu5003]跳舞的线【动态规划】的更多相关文章

  1. LG5003 跳舞的线 - 乱拐弯 线性DP

    问题描述 LG5003 题解 设 \(mx[i][j][0/1]\)代表当前位置.朝向的最大拐弯数,最小同理. 来源为左边和上边. 坑点:起点可能为#. \(\mathrm{Code}\) #incl ...

  2. Dancing Line、网易蜗牛读书——创新性分析

    Dancing Line——视听效果极佳的解压游戏 介绍:跳舞的线是由猎豹移动公司和BoomBitInc制作的一款游戏,发行于2016年12月12日. 游戏规则:跟着音乐的节奏点击屏幕,完成转向,躲避 ...

  3. 拥抱小程序,WeTest小程序全链路测试解决方案正式上线

    背景 随着微信开放小程序开发功能,迅速在各个实体店抢占流量入口,广大商家看到了在线和离线的机会整合,利用小程序版本特点低成本进入市场,达到流量的获取和转化. 伴随着资本的进入,小程序开发市场也因此越来 ...

  4. 【BZOJ5211】[ZJOI2018]线图(树哈希,动态规划)

    [BZOJ5211][ZJOI2018]线图(树哈希,动态规划) 题面 BZOJ 洛谷 题解 吉老师的题目是真的神仙啊. 去年去现场这题似乎骗了\(20\)分就滚粗了? 首先\(k=2\)直接算\(k ...

  5. BZOJ_3039_玉蟾宫_(动态规划+悬线法)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=3039 n*m的矩阵由R和F组成,求全是F的子矩阵的大小的三倍. 分析 悬线法: 浅谈用极大化思 ...

  6. UOJ#373. 【ZJOI2018】线图 搜索,树哈希,动态规划

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ373.html 前言 真是一道毒瘤题.UOJ卡常毒瘤++.我卡了1.5h的常数才过QAQ Orzjry 标算居然是指数做法 ...

  7. 洛谷P1169 [ZJOI2007]棋盘制作 悬线法 动态规划

    P1169 [ZJOI2007]棋盘制作 (逼着自己做DP 题意: 给定一个包含0,1的矩阵,求出一个面积最大的正方形矩阵和长方形矩阵,要求矩阵中相邻两个的值不同. 思路: 悬线法. 用途: 解决给定 ...

  8. [程序员代码面试指南]递归和动态规划-排成一条线的纸牌博弈问题(DP)

    题目 给定一个整型数组arr,代表数值不同的纸牌排成一条线.玩家A和玩家B依次拿走每张纸牌,规定玩家A先拿,玩家B后拿,但是每个玩家每次只能拿走最左或最右的纸牌,玩家A和玩家B都绝顶聪明.请返回最后获 ...

  9. [SinGuLaRiTy] 动态规划题目复习

    [SinGuLaRiTy-1026] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. [UVA 1025] A Spy in the Metr ...

随机推荐

  1. openstack-KVM-Network

    一.网络配置 1.查看网卡信息: lspci | grep Ethernet ethtool -i eth0 (qemu) info network virsh qemu-monitor-comman ...

  2. django的配置文件字符串是怎么导入的?

    写在开头: 每个APP都会有配置文件,像下代码Django等等这种的settings里面的配置导入都是字符串的,他们是怎么做的呢? MIDDLEWARE = [ 'django.middleware. ...

  3. alibaba druid

    FAQ · alibaba/druid Wikihttps://github.com/alibaba/druid/wiki/FAQ sql 连接数不释放 ,Druid异常:wait millis 40 ...

  4. ntpd、ntpdate、hwclock的区别

    hwclock --systohc 使用ntpdate更新系统时间 - 潜龙勿用 - CSDN博客https://blog.csdn.net/suer0101/article/details/7868 ...

  5. vue的三种传参方式

    <template> <div> <router-link :to="{'name':'x',params:{'type':'users'}}"> ...

  6. PHP中stdClass的意义

    在WordPress中很多地方使用stdClass来定义一个对象(而通常是用数组的方式),然后使用get_object_vars来把定义的对象『转换』成数组. 如下代码所示:   1 2 3 4 5 ...

  7. 进阶开发——文档,缓存,ip限速

    一.文档自动化管理 1.django rest framework提供了一个接口: 可以将代码中注释转换为文档中内容(list,create等),以及help_text等等,且会生成JavaScrip ...

  8. 关于jenkins旧的构建导致磁盘空间不足问题

    简述: Jenkins在每一次的执行构建后,都会对该构建的项目生成一个历史构建记录以及生成一份历史构建的项目发布包,长期累积可能会占用大量磁盘空间 jenkins构建jobs路径如下图: 解决办法: ...

  9. SpringBoot Junit Maven JaCoCo

    写一下最近写单体测试的一些笔记. SrpingBoot的测试用例: @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = ...

  10. 面对AI

    面对AI,我们应该怎么做? 李开复博士的一段话: 1. 我们应该具有战略性思维,并以人工智能无法取代的工作为目标.我们应该致力于终身学习,更新我们的技能,了解新趋势,并寻找新机遇. 2. 我们应该鼓励 ...