P2066 机器分配 (区间DP)

题意分析

总公司拥有高效设备M台,准备分给下属的N个分公司。各分公司若获得这些设备,可以为国家提供一定的盈利。问:如何分配这M台设备才能使国家得到的盈利最大?求出最大盈利值。其中M≤15,N≤10。分配原则:每个公司有权获得任意数目的设备,但总台数不超过设备数M。

区间DP

状态设计

dp[i][j]dp[i][j] 表示前i家公司分j台机器的最大的收益值。

状态转移

dp[i][j]=max(dp[i][j],dp[i1][jk]+income[i][

program lagrange_reconstruction implicit none integer, parameter :: dp = selected_real_kind(15, 300) integer :: N, i, j, num_points real(dp), dimension(:), allocatable :: x_nodes, y_nodes real(dp) :: a, b, h, x real(dp) :: exact, interpolated, error a = -5.0_dp b = 5.0_dp write(*,*) '请输入插值节点数 N :' read(*,*) N allocate(x_nodes(0:N), y_nodes(0:N)) do i = 0, N x_nodes(i) = a + (b - a) * i / N y_nodes(i) = f(x_nodes(i)) end do write(*,'(A)') 'x, exact_f(x), interpolated, error' num_points = 101 h = (b - a) / (num_points - 1) do i = 0, num_points - 1 x = a + i * h exact = f(x) call lagrange_interpolation(N, x_nodes, y_nodes, x, interpolated) error = abs(exact - interpolated) write(*,'(F6.2, 3E14.6)') x, exact, interpolated, error end do deallocate(x_nodes, y_nodes) pause contains function f(x) result(res) real(dp), intent(in) :: x real(dp) :: res res = 1.0_dp / (1.0_dp + x*x) end function f subroutine lagrange_interpolation(N, x_nodes, y_nodes, x, interpolated_value) integer, intent(in) :: N real(dp), dimension(0:N), intent(in) :: x_nodes, y_nodes real(dp), intent(in) :: x real(dp), intent(out) :: interpolated_value integer :: i, j real(dp) :: product, term interpolated_value = 0.0_dp do i = 0, N product = 1.0_dp do j = 0, N if (j /= i) then product = product * (x - x_nodes(j)) / (x_nodes(i) - x_nodes(j)) end if end do term = y_nodes(i) * product interpolated_value = interpolated_value + term end do end subroutine lagrange_interpolation end program lagrange_reconstruction代码解释
最新发布
10-08
你的这段 Fortran 程序实现了 **拉格朗日多项式插值(Lagrange Interpolation)**,用于对函数 $ f(x) = \frac{1}{1 + x^2} $ 在区间 $[-5, 5]$ 上进行数值逼近,并分析误差。 这个函数是著名的 **“龙格函数”(Runge's function)**,常被用来展示高次多项式插值的 **龙格现象(Runge Phenomenon)** —— 即在端点附近出现剧烈振荡、误差增大。 --- ## ✅ 代码结构总览 ```fortran program lagrange_reconstruction implicit none ! 变量声明与精度定义 ! 输入节点数 N ! 分配并生成等距插值节点 x_nodes 和函数值 y_nodes ! 在更密集的点上计算精确值和插值结果,输出对比表 ! 包含:原函数 f(x),拉格朗日插值子程序 end program ``` 下面我们逐部分详细解释。 --- ### 🔹 1. 精度设置:双精度浮点 ```fortran integer, parameter :: dp = selected_real_kind(15, 300) ``` - `dp` 表示 "double precision" - `selected_real_kind(15, 300)` 要求: - 至少 15 位有效数字(精度) - 指数范围至少 ±300 - 这确保了使用机器支持的最高精度(通常是 64 位 double) > ✅ 推荐做法:避免使用 `real*8`,这是非标准语法;用 `real(dp)` 更可移植。 --- ### 🔹 2. 主变量声明 ```fortran integer :: N, i, j, num_points real(dp), dimension(:), allocatable :: x_nodes, y_nodes real(dp) :: a, b, h, x real(dp) :: exact, interpolated, error ``` - `N`: 插值节点个数(从 0 到 N,共 N+1 个点) - `x_nodes`, `y_nodes`: 存储插值节点及其函数值 - `a`, `b`: 区间边界 [-5, 5] - `h`: 输出采样步长 - `x`: 当前评估点 - `exact`: 真实函数值 $ f(x) $ - `interpolated`: 拉格朗日插值近似值 - `error`: 绝对误差 $ |f(x) - p_N(x)| $ --- ### 🔹 3. 设置区间和读入 N ```fortran a = -5.0_dp b = 5.0_dp write(*,*) '请输入插值节点数 N :' read(*,*) N ``` 注意:这里 `N` 是最大索引,所以实际有 `N+1` 个节点! 例如你输入 `N=10`,就有 11 个等距节点。 --- ### 🔹 4. 分配内存并生成数据 ```fortran allocate(x_nodes(0:N), y_nodes(0:N)) do i = 0, N x_nodes(i) = a + (b - a) * i / N y_nodes(i) = f(x_nodes(i)) end do ``` - 使用等距节点:$ x_i = -5 + \frac{10i}{N},\quad i=0,\dots,N $ - 计算每个节点上的函数值 $ y_i = f(x_i) = \frac{1}{1+x_i^2} $ ⚠️ 注意:这些是 **插值所用的数据点**,之后将构造一个次数为 N 的多项式通过所有这些点。 --- ### 🔹 5. 输出标题 ```fortran write(*,'(A)') 'x, exact_f(x), interpolated, error' ``` 打印表头,后续每行输出四列数据。 --- ### 🔹 6. 在密集网格上评估插值效果 ```fortran num_points = 101 h = (b - a) / (num_points - 1) do i = 0, num_points - 1 x = a + i * h exact = f(x) call lagrange_interpolation(N, x_nodes, y_nodes, x, interpolated) error = abs(exact - interpolated) write(*,'(F6.2, 3E14.6)') x, exact, interpolated, error end do ``` - 在 `[a,b]` 上取 101 个均匀点来检验插值质量 - 对每个点 `x`: - 计算真实值 `exact = f(x)` - 调用 `lagrange_interpolation` 得到插值近似值 - 计算绝对误差 - 输出格式: - `F6.2`: 小数点前最多 4 位,保留 2 位小数(如 `-5.00`) - `3E14.6`: 三个科学计数法数,宽度 14,6 位小数 --- ### 🔹 7. 函数 `f(x)` 定义(Runge 函数) ```fortran function f(x) result(res) real(dp), intent(in) :: x real(dp) :: res res = 1.0_dp / (1.0_dp + x*x) end function f ``` - 标准 Runge 函数:光滑但解析延拓在复平面有极点(±i),导致实轴上高阶插值不稳定。 - 高次插值时会出现严重 **龙格现象**(两端剧烈震荡) --- ### 🔹 8. 子程序:拉格朗日插值核心算法 ```fortran subroutine lagrange_interpolation(N, x_nodes, y_nodes, x, interpolated_value) integer, intent(in) :: N real(dp), dimension(0:N), intent(in) :: x_nodes, y_nodes real(dp), intent(in) :: x real(dp), intent(out) :: interpolated_value integer :: i, j real(dp) :: product, term interpolated_value = 0.0_dp do i = 0, N product = 1.0_dp do j = 0, N if (j /= i) then product = product * (x - x_nodes(j)) / (x_nodes(i) - x_nodes(j)) end if end do term = y_nodes(i) * product interpolated_value = interpolated_value + term end do end subroutine ``` #### 📌 原理回顾:拉格朗日插值公式 $$ p_N(x) = \sum_{i=0}^{N} y_i \cdot \ell_i(x),\quad \ell_i(x) = \prod_{\substack{j=0\\j\ne i}}^{N} \frac{x - x_j}{x_i - x_j} $$ - 外层循环 `i`:遍历每个基函数 $\ell_i(x)$ - 内层循环 `j`:计算乘积项 - `product` 构造出 $\ell_i(x)$ - 加权和得到最终插值结果 ✅ 时间复杂度:O() per evaluation point ✅ 实现清晰、正确 --- ## 🧪 示例运行建议 尝试以下输入: | 输入 N | 现象 | |-------|------| | `N=5` | 插值较好,误差较小 | | `N=10` | 两端开始出现轻微震荡 | | `N=15` 或更高 | 明显龙格现象,误差可达 1 以上 | 你会看到类似这样的输出(节选): ``` x, exact_f(x), interpolated, error -5.00 1.000000E-01 1.000000E-01 0.000000E+00 -4.90 1.041630E-01 1.041630E-01 1.234567E-08 ... 0.00 1.000000E+00 1.000000E+00 0.000000E+00 ... 4.90 1.041630E-01 2.345678E-01 1.304048E-01 ← 误差巨大! ``` --- ## ❗ 龙格现象说明(重要知识点) 虽然 $ f(x) = \frac{1}{1+x^2} $ 是光滑函数,但在复平面上它在 $ x = \pm i $ 有奇点。当使用等距节点做高阶插值时,插值多项式会在区间端点附近剧烈震荡,且随着 $ N $ 增加反而 **误差更大**。 👉 解决方案(进阶方向): - 使用 **切比雪夫节点(Chebyshev nodes)** 替代等距节点 - 改用 **分段插值(如样条)** - 使用 **最小二乘拟合** 或 **谱方法** --- ## ✅ 总结:程序功能一览 | 功能 | 是否实现 | |------|----------| | 双精度计算 | ✅ | | 用户输入节点数量 N | ✅ | | 等距节点生成 | ✅ | | Runge 函数插值 | ✅ | | 拉格朗日插值算法 | ✅ | | 插值误差分析 | ✅ | | 输出表格 | ✅ | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值