方向标 | c++ | 动态规划

一、题目描述

A carpenter has received an order for a wooden directional sign. Each board must be aligned vertically with the previous one, either to the basis of the previous arrowhead or to the opposite side, being fixed there with a specially designed screw. The two boards must overlap. The carpenter wrote down a sequence of integers to encode the sketch sent by the designer but the sequence does not determine a unique model and he has thrown away the original sketch. What looked like a trivial task turned out a big jigsaw to him.

The sequence (with 1 + N elements) encodes the (N) arrows from the bottom to the top of the sign. The first element is the position of the left side of the bottom arrow. The remaining N elements define the positions where the arrowheads start, from bottom to top: the i-th element is the position where the i-th arrowhead basis is. For instance, the two signs depicted (on the left and on the right) could be encoded by 2 6 5 1 4.

Since a board must be aligned vertically with the previous one (either to the basis of the previous arrowhead or to the opposite side), if the sequence was 2 6 5 1 4 3, the fifth arrow could be fixed (in any of the depicted signs) with a screw either at 1 (pointing to the right) or at 4 (pointing to the left), with the arrowhead basis at 3.

If the sequence was 2 3 1, the second arrow could only be fixed with a screw at 3, pointing to the left, because consecutive boards must overlap.

 

All arrowheads are similar and the designer told the carpenter that their bases stand in different vertical lines, as well as the left side of the bottom arrow, altogether forming a permutation of 1..(N +1). That is why the carpenter overlooked the details and just wrote down the permutation (e.g., 2 6 5 1 4 3).

Given the sequence of numbers the carpenter wrote down, compute the number of directional arrows signs that can be crafted. Since the number can be very large, you must write it modulo 2147483647. The second integer in the sequence is always greater than the first one (the bottom arrow points to the right always).

Input

The first line has one integer N and the second line contains a permutation of the integers from 1 to N + 1. Integers in the same line are separated by a single space.

Output

The output has a single line with the number (modulo 2147483647) of distinct signs that can be described by the given permutation.

Note

1 ≤ N ≤ 2

测试输入期待的输出时间限制内存限制额外进程
测试用例 1以文本方式显示
  1. 2↵
  2. 2 3 1↵
以文本方式显示
  1. 1↵
1秒64M0
测试用例 2以文本方式显示
  1. 5↵
  2. 2 6 5 1 4 3↵
以文本方式显示
  1. 6↵
1秒64M0

二、题目翻译

        英文题目啊......翻成中文这事就交给你们手中的翻译软件了,我是说我先凝练一下题目在讲啥,它又要我们干啥。

首先为了方便,我这样描述一个木块:

        因为顶部和基只差一个单位长度,所以知道基就知道了顶部。也就是说要描述一个箭头长啥样,我只需要底部和基就行了。

题目向我们描述了这些信息:

①箭头和箭头是一块一块贴着搭上去的。

②从下往上搭的过程中,上面的箭头的底部,必须等于下面的箭头的底部,或者等于下面的箭头的基。

③相邻两个箭头需要有重叠部分,不然钉不住。

我们可以简单知道这些信息:

①相邻的两块箭头,因为上面的箭头的底部有两种可能放法,所以同样的数据自然可能有多种情况。

②题目就是要我们输出有多少种不同的情况。

题目输入共两行,第一行是木板的数量 n ,第二行的数据量为 n+1 ,其中第二行的第一个数据是最下面箭头的底部,之后的n个数据是第n-1行箭头的顶部。

题目要求输出可能的情况数%2147483647。


三、思路

这题难在......题目一开始看不懂。

看懂了以后,很容易想到递推。假设从下往上数第k块箭头的情况数为S(k),第(k+1)块箭头的情况数为S(k+1),S(k+1)和S(k)之间是有关系的。关键在于怎么计算S(k)。  


四、代码实现

注意几个点:

①假设 f[i][j] 是从下往上数第 i 块箭头,底部为 j 的情况数,显然S(i) = \sum_{j=1}^{n+1}f[i][j]

②第一块箭头的底部和顶部都已经确定,只有一种情况。

③当 i ≥ 2 时,第 i 块箭头的底部可能有 i 种情况,它们就是Input第二行的前 i 个数。

④鉴于 f[i][j] 可能会非常大(大到超过了2147483647),每一次最好都求一次余。

⑤懒得搞公式了,借用讨论区一张图

不清楚本人是否愿意被知晓名字,先不写。

限于博主的表达能力有限,这些个东西靠你们自己悟吧~


五、完整代码

#include<bits/stdc++.h> 
using namespace std;
typedef long long ll;
const int N = 2010;
ll f[N][N];    //f[i][j]表示第i个箭头,当底部为j的时候,可能的情况数。
int nd[N];    
//因为end存在关键字重复,这里写的是nd。nd[0]是最下面那一块的底部,之后nd[i]表示第i块的顶部

int n;
 
int main(){

	memset(f,0,sizeof(f));

	scanf("%d",&n);
	for (int i=0;i<=n;i++) {
		scanf("%d",&nd[i]);
	}
   
	f[1][nd[0]] = 1;     //第一块只有一种情况
	
	for (int i=2;i<=n;i++) { 
        //第i块

		for (int p=0;p<=i;p++) {
			int j = nd[p];
			if ( (j<nd[i]&&j<nd[i-1]) || (j>nd[i]&&j>nd[i-1]) ) {
				f[i][j] = f[i-1][j];
			}
			else if (j==nd[i-1]) {
				if (j<nd[i]) {
					for (int k=j+1;k<=n+1;k++) {
						f[i][j] += f[i-1][k];
					}
				}
				else {
					for (int k=1;k<=j-1;k++) {
						f[i][j] += f[i-1][k];
					}
				}
			}
			else f[i][j]=0;
			f[i][j] %= 2147483647;
		}

	}
	
	ll ans=0;
    
    //求第n块木板的情况总数
	for (int i=1;i<=n+1;i++) {
		ans += f[n][i];
		ans %= 2147483647;
	}
	cout << ans << endl;
	
	 
	return 0;
}

啧啧啧,这篇质量不高,看个乐呵就行。 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值