1443 拍照(枚举 + 推公式)

1. 问题描述:

农夫约翰在给他编号为 1…N 的 N 头奶牛排队拍照。约翰一开始计划从左向右数第 i 个位置排编号为 ai 的奶牛,他在一张纸上写下了排列 a1,a2,…,aN。不幸的是,这张纸刚刚被小偷偷走了!幸好约翰仍然有机会恢复他之前写下的排列。在这张纸被偷走之前,奶牛贝茜记录了序列 b1,b2,…,bN−1,对于每一个 1 ≤ i < N 满足 bi = ai + ai+1。基于贝茜的信息,帮助约翰恢复可以产生序列 b 的"字典序最小"的排列 a。排列 x 字典序小于排列 y:如果对于某个 j,对于所有 i < j 均有 xi = yi,且有 xj < yj(换句话说,这两个排列到某个位置之前都相同,在这个位置上 x 小于 y)。保证存在至少一个满足条件的 a。

输入格式

输入的第一行包含一个整数 N。第二行包含 N−1 个空格分隔的整数 b1,b2,…,bN−1。

输出格式

输出一行,包含 N 个空格分隔的整数 a1,a2,…,aN。

数据范围

2 ≤ N ≤ 10 ^ 3

输入样例:

5
4 6 7 6

输出样例:

3 1 5 2 4
样例解释
a 能够产生 b,因为 3+1=4,1+5=6,5+2=7,2+4=6。
来源:https://www.acwing.com/problem/content/1445/

2. 思路分析:

对于这种存在公式的题目我们需要先推导一下,发现一下其中的规律,我们可以写出所有的等式可以发现当bi确定之后实际上是一个n个未知数和n - 1个方程的线性方程组,根据线性方程组的相关知识可以知道方程组存在无穷多解或者无解的情况,而题目中规定了至少存在一组解,所以肯定是有解的。n个未知数n-1个方程所以肯定存在一个自由变量,不妨设a1为自由变量所以当a1的值确定之后,那么a2~an的值也是确定的,所以我们可以枚举a1的所有可能取值,也即枚举1~n,通过下图中的公式递推出a2~an,因为题目中要求a1~an是一个排列所以在递推ai的过程中判断生成的ai是否符合要求,如果当n个元素ai都符合要求那么直接返回结果即可,因为第一次找到的满足要求的一定是字典序最小的。

3. 代码如下:

from typing import List


class Solution:
    # 判断当前的a1推导出的其余ai是否是一个排列
    def check(self, a1: int, n: int, b: List[int]):
        # st用来标记对应的数字是否已经被使用过了
        st = [0] * (n + 10)
        a = [0] * (n + 10)
        a[1] = a1
        st[a1] = 1
        for i in range(2, n + 1):
            # 推导出其余的ai
            a[i] = b[i - 1] - a[i - 1]
            # 越界了
            if a[i] < 0 or a[i] > n: return False
            # 存在重复
            if st[a[i]]: return False
            st[a[i]] = 1
        # 没有重复没有越界且总共有n个元素说明是一个排列输出排列
        for i in range(1, n + 1):
            print(a[i], end=" ")
        return True

    # 枚举 + 推公式
    def process(self):
        n = int(input())
        # b的前面加上一个0这样下标可以从1开始比较好处理
        b = [0] + list(map(int, input().split()))
        for i in range(1, n + 1):
            if self.check(i, n, b):
                break


if __name__ == "__main__":
    Solution().process()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值