• 27.方向标


    题目

    描述

    一位木匠收到了一个木制指示牌的订单。每块木板必须与前一块垂直对齐,要么与前一个箭头的基部对齐,要么与相反的一侧对齐,在那里用特制的螺钉固定。两块木板必须重叠。木匠将设计师发送的草图编码成了一个整数序列,但该序列不能确定唯一的模型,他已经把原始草图扔掉了。这看起来像是一个微不足道的任务,但对他来说却是一个巨大的拼图。

    序列(有1 + N个元素)将(N)个箭头从底部到顶部的位置进行编码。第一个元素是底部箭头左侧的位置。剩余的N个元素定义了箭头头部的起始位置,从底部到顶部:第i个元素是第i个箭头基部的位置。例如,左侧和右侧的两个标志可以通过2 6 5 1 4进行编码。

    由于木板必须与前一块垂直对齐(要么与前一个箭头的基部对齐,要么与相反的一侧对齐),如果序列是2 6 5 1 4 3,第五个箭头可以用一个螺钉固定(在任何一个描绘的标志上)在1(指向右边)或4(指向左边),箭头基座在3。

    如果序列是2 3 1,第二个箭头只能用一个螺钉固定在3上,指向左边,因为连续的木板必须重叠。

    所有的箭头头部都是相似的,设计师告诉木匠,它们的基部站在不同的垂直线上,底部箭头的左侧也是如此,共同形成1..(N +1)的排列。这就是为什么木匠忽略了细节,只是写下了排列(例如2 6 5 1 4 3)。

    给定木匠写下的数字序列,计算可以制作的指示牌数量。由于数字可能非常大,你必须对2147483647取模。序列的第二个整数始终大于第一个整数(底部的箭头总是指向右边)。

    输入

    第一行有一个整数N,第二行包含从1到N + 1的整数的排列。同一行中的整数之间用一个空格分隔。

    输出

    输出一行,其中包含给定排列描述的不同标志的数量(对2147483647取模)。

    注意

    1 ≤ N ≤ 2000


    题意

    1. 只需关注箭头长方形部分的头部(靠近三角形)和底部。
    2. 箭头从下往上依次放置。
    3. 第一个箭头的头部和底部题目已经确定,并且头部下标一定大于底部。
    4. 上面的箭头的底部,必须等于下面的箭头的底部,或者等于下面的箭头的头部。
    5. 相邻两个箭头需要有重叠部分,即不能错开。

    由此推知

    1. 对于当下箭头而言,上一块箭头的头部和底部很重要。
    2. 上面的箭头的底部可能有多种放法
    3. 题目就是要我们输出有多少种不同的情况。
    4. 要求对输出结果%2147483647。
       

    思路(动态规划)

    我们用数组s[N+1]记录每一块木块的头部下标。注意:s[0]存储第一块的底部下标

    第一步:确定dp数组以及下标的含义

    我们需要一个二维dp数组,dp[i][j]表示第i个箭头以j为底时的方案数。

    第二步:确定递推公式

    3种情况:

    • 第i块的头比上一块的头和尾都小
      dp[i][max(s[i - 1], s[j])] += dp[i - 1][s[j]];
    • 第i块的头比上一块的头和尾都大
       dp[i][min(s[i - 1], s[j])] += dp[i - 1][s[j]]; 
    • 第i块的头在上一块头尾之间
      dp[i][s[i - 1]] += dp[i - 1][s[j]];
      dp[i][s[j]] += dp[i - 1][s[j]];

    第三步:dp数组初始化

    第一个箭头只有一种放的方法:dp[1][s[1]]=1

    遍历每一个箭头时,令dp[i][s[i]]=1,以本身头部为底时为一。结果记得减一

    第四步:确定遍历顺序

    外循环从第二个箭头开始,内循环遍历上一个箭头所有可能底部。
    如果dp[i-1][j]>0,说明上一个箭头存在以该底部放置的方案,方可进入递推。

    第五步:结果处理

    遍历dp[N],把每一个底部的方案加起来就是结果。


    注意事项

    1. long long存储数据。
    2. 最后结果和每次dp变化之后和要取模

    C++完整代码

    改改再提交乐学,有查重!!

    1. #include
    2. #include
    3. using namespace std;
    4. const int MOD = 2147483647;
    5. int main() {
    6. int N;
    7. cin >> N;
    8. vector<long long> s(N + 1);
    9. for (int i = 0; i <= N; i++) {
    10. cin >> s[i];
    11. }
    12. vectorlong long>> dp(N + 1, vector<long long>(2001, 0));
    13. dp[1][s[0]] = 1;
    14. dp[1][s[1]] = 1;
    15. for (int i = 2; i <= N; i++) {
    16. for (int j = i - 2; j >= 0; j--) {
    17. dp[i][s[i]] = 1;
    18. if (dp[i - 1][s[j]] != 0) {
    19. if (s[i] > s[i - 1] && s[i] > s[j]) {
    20. dp[i][min(s[i - 1], s[j])] += dp[i - 1][ s[j]];
    21. dp[i][min(s[i - 1], s[j])] %= MOD;
    22. }
    23. else if (s[i] 1] && s[i] < s[j]) {
    24. dp[i][max(s[i - 1], s[j])] += dp[i - 1][s[j]];
    25. dp[i][max(s[i - 1], s[j])] %= MOD;
    26. }
    27. else if ((s[i] <= s[i - 1] && s[i] >= s[j]) || (s[i] >= s[i - 1] && s[i] <= s[j])) {
    28. dp[i][s[i - 1]] += dp[i - 1][s[j]];
    29. dp[i][s[i - 1]] %= MOD;
    30. dp[i][s[j]] += dp[i - 1][s[j]];
    31. dp[i][s[j]] %= MOD;
    32. }
    33. }
    34. }
    35. }
    36. long long res = 0;
    37. for (int i = 0; i <= 2000; i++) {
    38. res += dp[N][i];
    39. res %= MOD;
    40. }
    41. cout << res-1 << endl;
    42. return 0;
    43. }

  • 相关阅读:
    老板让你Excel统计数据无从下手?没事,ChatGPT来帮你!
    【多线程】阻塞队列 详解
    树莓派无需显示屏的VNC Viewer方式的远程连接
    day10_面向对象_抽象_接口
    项目部署spring
    Linux Server 终止后立即重启报错 bind error: Address already in use
    Prometheus Operator 配置PrometheusRule告警规则
    MySQL 流程控制
    C++学习笔记(十九)
    嵌入式Linux应用开发-Framebuffer 应用编程
  • 原文地址:https://blog.csdn.net/m0_74200772/article/details/132729940