1197 山区建小学

本文详细记录了解决1197题——山区建小学的过程,包括读题、思考、理解代码的时间,并强调了这道题目在递推问题中的复杂性。通过构建距离矩阵,找到两地到学校的最短距离,然后利用三层循环进行状态转移,求解从i个村庄建立j个学校的最短距离。对于递推式中涉及的细节,如为何使用j-1而非j,作者也进行了深入探讨。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1197 山区建小学

读题20分钟,想题10分钟,看代码20分钟,理解代码20分钟,写代码10分钟,调代码40分钟…(逃
个人感觉是练的递推里面比较复杂的,应该是一道绿题吧,光递推式就很难找
设状态a表示两地距离,c表示两地到学校最近距离,f表示从i个村庄建立j个学校的最短距离
首先我们要根据两两之间的距离来构造各个点的距离
然后通过中间点距离最短计算两个点到学习的最近距离和,也即是c数组
然后就是求f数组,用一个n3循环枚举f数组,递推式

f[i][j]=min(f[i][j],f[k][j-1]+c[k+1][i]); 

他的思路就是枚举一个k,将ij区间分成两个部分,以k为中点;f[k][j-1]前面表示k个村建立j-1个学校最小和,c[k+1][i]表示第k个村庄到第i个村庄的最小距离
至于为什么是j-1而不是j,这个想了很长时间,因为当前的j是由前面的状态来决定的

#include<iostream>
#include<cstdio>
#include<algorithm> 
using namespace std;
int a[505][505],c[505][505];//a表示两地距离
//c表示两地到学校最近距离 
int f[505][505];//f表示从i个村庄建立j个学校的最短距离 
int main()
{
   
   
	int n,m;//n个村庄 选m个村建学校 
	cin>>n>>
### 关于信息学奥赛 NOIP “山区小学” 的解法与思路 #### 问题描述 题目通常涉及在一个一维数组上选择若干位置立学校,使得所有村庄到最近学校的距离之和最小化。这是一个经典的优化问题。 #### 解决方法概述 该问题可以通过动态规划来解决。以下是详细的分析: 1. **状态定义** 定义 `dp[i][k]` 表示前 `i` 个村庄立了 `k` 所学校时的最优解(即总距离最短)。其中,`i` 是当前考虑的村庄编号,`k` 是已经立的学校数量[^2]。 2. **转移方程** 对于每一个子问题 `dp[i][k]`,可以枚举第 `k` 所学校的位置 `j` (满足 `j ≤ i`),则有: \[ dp[i][k] = \min_{j} (dp[j-1][k-1] + cost(j, i)) \] 其中,`cost(j, i)` 表示从村庄 `j` 到村庄 `i` 如果只设立一所学校,则这些村庄到这所学校的总距离。 3. **预处理成本函数** 计算任意区间 `[l, r]` 内如果设置一所学校所需的总代价 `cost(l, r)` 可以通过二分查找找到最佳学校位置并计算相应距离。为了加速这一过程,可以利用前缀和技巧预先计算每两个村庄之间的绝对差值累积和。 4. **初始化条件** 当仅有一个村庄时 (`i=1`) ,无论造多少所学校,其初始值都应设为零;当没有任何学校被设时(`k=0`),除了第一个村庄外其他地方的距离均为无穷大或者极大值表示不可达情况。 5. **最终答案获取** 结果存储在 `dp[n][m]` 中,这里 `n` 是总的村庄数目而 `m` 是允许的最大学校数。 ```python def solve(): n, m = map(int, input().split()) # 村庄数 和 学校数 pos = list(map(int, input().split())) # 每个村庄的位置 # 排序以便后续操作 pos.sort() # 初始化 cost 数组 cost = [[0]*(n+2) for _ in range(n+2)] prefix_sum = [0] * (n+1) for i in range(1, n+1): prefix_sum[i] = prefix_sum[i-1] + pos[i-1] def get_cost(l, r): # l,r 已经加1偏移量调整过了 mid = (l+r)//2 total = prefix_sum[r]-prefix_sum[l-1] median_pos = pos[mid-1] count = r-l+1 return abs(total - count*median_pos) for length in range(2,n+1): for start in range(1, n-length+2): end = start + length -1 cost[start][end]=get_cost(start,end) INF=float('inf') dp=[[INF]*(m+1)for _ in range(n+1)] for k in range(m+1): dp[0][k]=0 for i in range(1,n+1): for k in range(1,min(i,m)+1): for j in range(k,i+1): dp[i][k]= min(dp[i][k], dp[j-1][k-1]+cost[j][i]) print(dp[n][m]) ``` #### 时间复杂度分析 上述实现的时间复杂度主要由三重循环决定:外部两层遍历所有的 `(i,k)` 组合,内部一层用于寻找分割点 `j` 。因此总体时间复杂度大约为 O(N^2M),对于较小规模的数据集是可以接受的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值