P5017题解
前言
做这道题,首先要了解
- 根据题意确定状态。
- 根据状态的定义推出状态转移方程,一般有两种:填表法和刷表法。填表法就是普通
,用前面的状态转移到现在的状态,例: 。刷表法就是在现有的基础上( 已知),去推出 ( ),例: 。 - 处理边界并输出。边界问题是
中非常重要的部分,且每道题的边界不同,要视题意而定。
注:此题采用刷表法。
暴力
对于每道题,首先考虑暴力。那么这道题呢,首先你必须想到的就是需要排序,毕竟排序后车子在每个时间段接的人你才能知道。排完序后,试着用
试着推状态转移方程:
当我们发现一维不行时,就定义二维状态转移方程。根据一维的推导过程中发现,是因为我们不知道
正解
我们现在的问题是第二维,那么我们考虑优化第二维:细心观察数据发现,
所以我们不妨优化定义:
那么状态转移方程就是:
-
先看
: 表示的是第 个人的等候时间,且由 转移而来,所以 等于 的上车时间加上车子的往返时间减去自己到达的时间(这里一定要弄懂), 的等候时间就是 , 数组表示 到达的时间, 是 等待时间,所以 ,其中 就是车子在送完 后回到起点的时间,用这个时间减去 到达的时间,不就是 的等候时间吗? -
再看
: 表示 ~ 这段区间内所有人的等候时间总和。这个也很好想,考虑一个事实, 。比如:结束时间为 ,有两个人,他们到达时间为 ,最后的等待时间就是 。那么这道题的结束时间就是 ,就是到达时间( )加上等候时间( ),人数就是( ),他们等待时间之和可以用前缀和算出来,就是 , 是前缀和数组。那么这道题也就完了呢。 -
状态转移方程:
。 -
边界问题:先初始化为了正无穷,
为 。 -
细节问题看代码。
AC code
#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
using namespace std;
typedef pair<int,int> pii;
const int N=510,M=110;
int inf;
int n,m;
int f[N][M];
int a[N],t[N];
signed main(){
memset(f,0x3f,sizeof f);
inf=f[0][0],cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
for(int i=1;i<=n;i++) t[i]=t[i-1]+a[i];
f[0][0]=0,a[0]=-inf;
for(int i=0;i<=n;i++){
int M=min(m-1,a[i+1]-a[i]);
//优化时间和空间,j只用枚举到m-1,因为前面的数会跟着前面的车走
//a[i+1]-a[i]表示这个点到下一个点的时间差,因为一旦与下一个点的距离不大于m-1,就可以同时乘坐同一次车
for(int j=0;j<=M;j++){
if(f[i][j]==inf) continue;
for(int k=i+1;k<=n;k++){
int l=max(a[i]+j+m-a[k],0ll);//等待时间
f[k][l]=min(f[k][l],f[i][j]+(a[k]+l)*(k-i)-(t[k]-t[i]));
//状态转移方程式
}
}
}
int ans=inf;
for(int i=0;i<m;i++) ans=min(ans,f[n][i]);//最后取值
cout<<ans<<endl;
return 0;
}/*
f[i][j]表示前i个人等了j时刻上车时的最小等候时间(滚动数组,降低时间与空间复杂度)
f[k][l]=min(f[k][l],f[i][j]+(t[k]+l)*(k-i)-(t[k]-t[i]));
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端