POJ 2671 Jimmy's Bad Day题解(很详细很友好,类似区间dp)
有问题的话欢迎在评论区提出
题意:
题目链接
你是一个送快递的,现在给你一个环,环的边有权值,代表走这条边所花的时间,每个点代表一个地点,点有点权,代表这个点上有多少货物需要你送。初始时间,每到一个点,你就可以瞬间送完该点所有的货物,但每一个货物都会给你带来值为当前时间的罚款。现在你要送完所有货物,问最优情况下你的罚款最少是多少。
题解:
从样例可以看出,这题的核心就在于,快递员可以“反复横跳”,比如可以先逆时针送完一个点的货物,再掉头沿顺时针方向送完所有其它点的货物(尽管这样使得一条边经过了两次,但使得总罚款更少了)。
为了方便下面的说明,先约定些定义:我把起点记做0号点,0号点逆时针走格到达的点叫做“逆时针第点”,0号点顺时针走格到达的点叫做“顺时针第点”;
例如,下图中,4号点是顺时针第4点与逆时针第2点。
废话不多说,直接看状态的定义:
:如果的时候你站在顺时针号点,送完包括端点在内,整个区间的最小罚款;
:如果的时候你站在逆时针号点,送完包括端点在内,整个区间的最小罚款;
有两点值得注意的:
- 状态定义中假设了时的位置,但事实上的时候你是在起点的。为何这么假设呢?往下看就知道了;
- 上述所谓区间,表示的是由i顺时针到j的区间(划重点,也就是说,该区间走的是不含起点的那条路!),这里为了便于理解说成是,下面也用这种方式表示区间和点,不要搞混了。
那么仍以下图为例,取和,看看如何更新;
再次强调,该图里的区间是这一段
可以通过如下两种方式更新(特别提醒,由于时你就在顺时针第个位置,所以处的货物不带来任何罚款,这也是下面两种情况中我们都无视了点罚款的原因):
- 走到,这样做的花费是 : ( 区间的货物数量之和 与点间的时间 )
解释:假设的时候你在处,而的时刻假设你在处,因此,两者的“开始时间”有个差距,即“到的时间”,该时间的影响是平均的作用在该区间所有货物上的。- 掉头走到,这样做的花费是 : ( 区间的货物数量之和 掉头走到点的时间 )
解释:与上面的情况对比,仍然是中的所有货物都统一的被拖延了一个时间,只不过这次该时间变成了到的一条路的时间
最终,的值就取这两者与自己之间的最小值,就可以完成更新。
的更新同理,可以参考下面dfs函数内写的方法。
后记:
最开始,我定义的是“由逆时针到全部送完的最小罚款”,也就是走含起点的那一条路,相信应该会有一些人第一反应也这么想吧。但这样搞的话,似乎就没法转移了,因为你没法处理掉头多次的情况。
总之,第一反应能想到一个正确的状态,真的很重要。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int inf=0x3f3f3f3f;
int n,num[310],len[310],lpre[310],npre[310];
int dp[310][310][2];
int nsum(int i,int j){
int tmp=(i!=0);j=n-j;
return npre[j]-npre[i-tmp];
}
int lsum(int i,int j){
return lpre[j]-lpre[i];
}
int dfs(int i,int j,int pos){
if(dp[i][j][pos]!=inf){
return dp[i][j][pos];
}
if(i+j>=n){
return dp[i][j][pos]=0;
}
if(pos==1){
dp[i][j][1]=min(dp[i][j][1],dfs(i,j+1,1)+len[n-(j+1)]*nsum(i,j+1));
dp[i][j][1]=min(dp[i][j][1],dfs(i,j+1,0)+(lpre[i]+lsum(n-j,n))*nsum(i,j+1));
}
else{
dp[i][j][0]=min(dp[i][j][0],dfs(i+1,j,0)+len[i]*nsum(i+1,j));
dp[i][j][0]=min(dp[i][j][0],dfs(i+1,j,1)+(lpre[i]+lsum(n-j,n))*nsum(i+1,j));
}
return dp[i][j][pos];
}
int main(){
while(~scanf("%d",&n)){
if(!n) break;
for(int i=0;i<n;i++){
scanf("%d%d",&num[i],&len[i]);
}
lpre[0]=npre[0]=num[n]=0;
for(int i=1;i<=n;i++){
lpre[i]=lpre[i-1]+len[i-1];
npre[i]=npre[i-1]+num[i];
}
memset(dp,inf,sizeof(dp));
int ans=min(dfs(0,0,0),dfs(0,0,1));
printf("%d\n",ans);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现