P1220 关路灯
P1220 关路灯
题目翻译:
给定一段路,路上每个灯的位置,和每个灯的功率。并给出老张的初始位置,求出一个方案,使得老张关完所有灯时所消耗的电量最小,并输出最小电量
思路:
只是一道很明显的区间\(dp\),有与每关完一个区间内的灯时,老张可嫩在这个区间的左边或右边,那我令\(dp[i][j][op]\),表示关完\(i,j\)这一区间的等后,老张在\(op\)边时的最小耗电量。当\(op=0\)时表示在左边,\(op=1\)时表示在右边。接下来分类思考:
\(1.\)若在左边的话,那老张有两种方式到达:
\((1)\)直接从\(i+1,j\)左边转移到\(i,j\),也就时不转弯,那么耗电量为
\[$dp[i][j][0]=dp[i+1][j][0]+(p[i+1]-p[i])*cost$ \]其中\(p[i]\)表示第\(i\)个灯的距离,\(cost\)表示剩余路灯消耗功率。
\((2)\)若它上个区间结束与右边的话,那就要转弯,则耗电量为\[$dp[i][j][0]=dp[i+1][j][1]+(p[j]-p[i]) \times cost$ \]那当前状态的动态转移方程为其两值求最小,则为:
\[$dp[i][j][0]=min(dp[i+1][j][0]+(p[i+1]-p[i]) \times cost,dp[i+1][j][1]+(p[j]-p[i]) \times cost)$ \]若我们提前运用前缀和,预处理了\(use[i][j]\),表示已经关完\(i,j\)的灯之后的每秒耗电量,那转移方程可以变为:
\[$dp[i][j][0]=min(dp[i+1][j][0]+(p[i+1]-p[i]) \times use[i+1][j],dp[i+1][j][1]+(p[j]-p[i]) \times use[i+1][j])$ \]
同理可得若要到右边,则:
\[$dp[i][j][1]=min(dp[i][j-1][1]+(p[j]-p[j-1]) \times use[i][j-1],dp[i][j-1][0]+(p[j]-p[i]) \times use[i][j-1])$ \]及把顺序调换一遍即可
实现:
\(1.\)只需要先维护一前缀和\(sum[i]\)在循环求出\(use[i][j]\)(注意:要给\(dp\)初始化到最大,并把\(dp[c][c][0]\)和\(dp[c][c][1]\)初始化为零,因为老张开局就会关掉)
memset(dp,0x3f,sizeof(dp));
for(int i=1;i<=n;i++){
cin>>p[i]>>w[i];
sum[i]=sum[i-1]+w[i];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
use[i][j]=sum[n]-sum[j]+sum[i-1];
}
}
dp[c][c][0]=dp[c][c][1]=0;
\(2.\)在枚举\(i,j\),(注意:\(i\)要从\(c\)开始向有枚举),运用转移方程求出答案即可
for(int i=c;i>=1;i--){
for(int j=i+1;j<=n;j++){
dp[i][j][0]=min(dp[i+1][j][0]+(p[i+1]-p[i])*use[i+1][j],dp[i+1][j][1]+(p[j]-p[i])*use[i+1][j]);
dp[i][j][1]=min(dp[i][j-1][1]+(p[j]-p[j-1])*use[i][j-1],dp[i][j-1][0]+(p[j]-p[i])*use[i][j-1]);
}
}
\(3.\)输出时记得将左右端点的答案比较一下输出
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=200;
int w[N],p[N],sum[N];
int dp[N][N][2];
int use[N][N];
int main(){
int n,c;
cin>>n>>c;
memset(dp,0x3f,sizeof(dp));
for(int i=1;i<=n;i++){
cin>>p[i]>>w[i];
sum[i]=sum[i-1]+w[i];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
use[i][j]=sum[n]-sum[j]+sum[i-1];
}
}
dp[c][c][0]=dp[c][c][1]=0;
for(int i=c;i>=1;i--){
for(int j=i+1;j<=n;j++){
dp[i][j][0]=min(dp[i+1][j][0]+(p[i+1]-p[i])*use[i+1][j],dp[i+1][j][1]+(p[j]-p[i])*use[i+1][j]);
dp[i][j][1]=min(dp[i][j-1][1]+(p[j]-p[j-1])*use[i][j-1],dp[i][j-1][0]+(p[j]-p[i])*use[i][j-1]);
}
}
cout<<min(dp[1][n][0],dp[1][n][1]);
}
区间\(DP\)讲解
本文作者:XichenOC
本文链接:https://www.cnblogs.com/XichenOC/p/18682392
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步