Project_Euler-24 题解

Project_Euler-24 题解

标题

题目

1
2

思路

如果是用笔算的话,可以找到这样的规律:

对于下面的这个组合:

0123456789 {0123456789} 0123456789

我们将其看作一种排列情况,然后我们先使用微扰的方式更改变动一下其中的数值,具体这样来做:

根据字典序的顺序,我们让他变动4次,记录每次变换的规律:

1:
01234567 9 8 01234567\textcolor {red}{9}8 0123456798

0123456 8 79 0123456\textcolor {red}{8}79 0123456879

0123456897 0123456897 0123456897

0123456 9 78 0123456\textcolor {red}{9}78 0123456978

发现一个这样的规律,越高位的数字,越难变动。最后一位数字每次都会变动,肯定是存在规律在其中的。

具体是什么样的规律呢?

假设一共有 N N N 位,如果我们想让倒数第 K K K 位增加1,那么从 K + 1 K + 1 K+1~ N N N位的数字位数都需要变换一遍,如果我们一位一位的确定,那么总共有如下的可能性:

C N − K 1 ∗ C N − K − 1 1 ∗ C N − K − 2 1 . . . C 1 1 C_{N-K}^{1} * C_{N-K-1}^{1}*C_{N-K-2}^{1}...C_{1}^{1} CNK1CNK11CNK21...C11

例如:$012,
如果我们想让最高位变动,那么就需要 C 3 − 1 1 ∗ C 3 − 2 1 = C 2 1 ∗ C 1 1 = 2 ∗ 1 = 2 C_{3-1}^{1}*C_{3-2}^{1} = C_{2}^{1}*C_{1}^1{} = 2 * 1 = 2 C311C321=C21C11=21=2 次变动。

最终我们发现,如果想让第 K K K 位按照字典序变动一次,那么就需要让后面所有位变动 ( N − K ) ! (N-K)! (NK)!位。

根据这个特点,我们可以判断出对于 0123456789 0123456789 0123456789 这串数列每一位想要变动时,后面需要变动的次数。

他们分别是:

9 ! , 8 ! , 7 ! , 6 ! , 5 ! , 4 ! , 3 ! , 2 ! , 1 ! , 0 {9!,8!,7!,6!,5!,4!,3!,2!,1!,0} 9!,8!,7!,6!,5!,4!,3!,2!,1!,0

现在题目想让我们求出第一百万位的的排列是什么,由于 0123456789 0123456789 0123456789 是第一种情况,因此我们实际需要变动 999999 999999 999999 次,我们可以通过上面的方式从最高位开始往后确定。

每一位应当尽可能的变化:

例如第一位, 9 ! = 352880 9! = 352880 9!=352880 所以可以让其变换 2 2 2 次,第一位的值就等于 2 2 2,此时 999999 − 2 ∗ 9 ! = 999999 − 725760 = 274239 999999 - 2 * 9! = 999999 - 725760 = 274239 99999929!=999999725760=274239;

第二位, 8 ! = 40320 8! = 40320 8!=40320,可以让其变换 6 6 6 次,第二位的值就等于 6 6 6, 此时 274239 − 6 ∗ 8 ! = 32319 274239 - 6 * 8! = 32319 27423968!=32319

依次类推。

值得一提的是,对于每一位变换的时候,初始值应该是多少呢?我们可以开辟一个数组,对每一位进行标记,如果使用了,就记为1,每用过就记为0,在变换时,我们从0开始,如果这个值被用过,那么向后跳一位,但是不计入变换次数。

代码

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#define MAX_N 10

// jump用来记录1到9的阶乘
int jump[MAX_N + 5];
// 用于标记哪些数字没被使用过, 1表示没被使用,0表示使用过了
int empty_num[MAX_N + 5];

// 初始化函数,计算阶乘,初始化empty_num数组
void init() {
    jump[0] = 1, empty_num[0] = 1;
    for (int i = 1; i <= MAX_N; i++) {
        jump[i] = jump[i - 1] * i;
        empty_num[i] = 1;
    }
    return ;
}

// 这个函数返回在需要变换k次时,第n位的值应该是多少。
int get_num(int n, int k) {
	// ind记录应该变化的位数,i表示从-1开始
    int ind = (k / jump[n]) + 1, i = -1;
    while(ind) {
    	// i先加,从0开始判断
        i++;
        // 被用过的数字不会对ind造成影响
        ind -= empty_num[i];
    }
    // 标记使用过
    empty_num[i] = 0;
    return i;
}

int main() {
    init();
    int n, k;
    // n表示需要判断的位数,k表示要求的排列这里应该是一百万。
    scanf("%d %d", &n, &k);
    k -= 1;
    // 从最高位开始判断
    for (int i = n - 1; i >= 0; --i) {
        int num = get_num(i, k);
        printf("%d", num);
        k %= jump[i];
    }
    printf("\n");
    return 0;
}
  • 29
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我可以为您讲解一下拉格朗日方程的推导过程,以一维情况为例。 假设一个质点在一维坐标系上运动,其位置用 $q$ 表示,时间用 $t$ 表示。该质点的运动可以用拉格朗日函数 $L(q, \dot{q}, t)$ 描述,其中 $\dot{q}=\frac{dq}{dt}$ 表示质点的速度。 为了推导欧拉-拉格朗日方程,我们首先需要定义一个重要的量,即作用量 $S$。作用量定义为 $$S = \int_{t_1}^{t_2} L(q, \dot{q}, t) dt$$ 其中 $t_1$ 和 $t_2$ 表示质点运动的起点和终点。作用量可以理解为一个积分,它是拉格朗日函数在时间 $t_1$ 到 $t_2$ 内的时间积分。 接下来,我们需要考虑如何对作用量进行变分。变分是一种数学操作,它类似于求导,但是是对函数进行微小的偏移,即对函数进行微小的扰动。对于一个作用量 $S$,其变分可以表示为 $$\delta S = S[q+\delta q] - S[q]$$ 其中 $\delta q$ 表示对 $q$ 进行微小的扰动。 现在我们来考虑如何对作用量进行变分。首先,我们将作用量中的积分拆开,得到 $$\delta S = \int_{t_1}^{t_2} \left( \frac{\partial L}{\partial q} \delta q + \frac{\partial L}{\partial \dot{q}} \delta \dot{q} \right) dt$$ 其中第一个式子中的 $\frac{\partial L}{\partial q}$ 表示对 $L$ 关于 $q$ 的偏导数,第二个式子中的 $\frac{\partial L}{\partial \dot{q}}$ 表示对 $L$ 关于 $\dot{q}$ 的偏导数。 现在我们需要将 $\delta \dot{q}$ 转化为 $\delta q$。由于 $\dot{q}=\frac{dq}{dt}$,我们可以得到 $$\delta \dot{q} = \frac{d}{dt} \delta q$$ 将上式代入到 $\delta S$ 中,得到 $$\delta S = \int_{t_1}^{t_2} \left( \frac{\partial L}{\partial q} \delta q + \frac{\partial L}{\partial \dot{q}} \frac{d}{dt} \delta q \right) dt$$ 接下来,我们需要对第二个式子进行分部积分,得到 $$\delta S = \int_{t_1}^{t_2} \left( \frac{\partial L}{\partial q} - \frac{d}{dt} \frac{\partial L}{\partial \dot{q}} \right) \delta q dt + \left[ \frac{\partial L}{\partial \dot{q}} \delta q \right]_{t_1}^{t_2}$$ 现在我们需要对第二个式子进行讨论。由于质点在运动时,其位置 $q$ 和速度 $\dot{q}$ 在起点和终点上都是确定的,因此 $\delta q$ 在 $t_1$ 和 $t_2$ 处的值都应该为 0。因此,第二个式子等于 0。 最终,我们得到了欧拉-拉格朗日方程: $$\frac{d}{dt} \frac{\partial L}{\partial \dot{q}} - \frac{\partial L}{\partial q} = 0$$ 这个方程描述了质点的运动。如果我们能够求出拉格朗日函数 $L$,那么欧拉-拉格朗日方程就可以帮助我们计算质点的运动。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若亦_Royi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值