我们登上的|

XichenOC

园龄:1个月粉丝:4关注:0

📂题解
🔖DP
2025-01-20 19:30阅读: 5评论: 0推荐: 0

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 中国大陆许可协议进行许可。

posted @   XichenOC  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起