P1220 关路灯

P1220 关路灯

题意:在一个一维坐标轴上有n盏灯,每盏灯有自己的坐标和功耗(单位:m和w)。有一个人,其初始位置为c(保证c有灯),他首先关掉初始位置的灯,然后向左或向右去关掉所有的灯(速度:1m/s,关灯不花费时间),求最后关掉所有灯花费的最小总功耗(单位:j)。

思路:区间dp,开三维dp[i][j][k]表示区间(i,j)内的灯全部关闭的最小花费(区间外的灯都是开的),k=0时表示人在这个区间的左端点,k=1则在右端点。

状态的转移:

        对于在左端点的状态dp[i][j][0]可以由子区间(i+1,j)转移。从区间(i+1,j)扩张到区间(i,j)可以通过左端点i向左走一个灯的距离或者右端点j向左走j-i个灯的距离。最优状态在两个端点肯定可以取到,不用考虑中间的位置。

        转移方程:dp[i][j][0]=min(dp[i+1][j][0] + 功耗花费1,dp[i+1][j][1] + 功耗花费2)

        功耗花费 = 走路时间*开着的灯的总功率

        走路时间直接相减就可以得到;开着的灯的总功率可以用前缀和来快速计算,所有灯的总功率 - (i+1,j)区间内的功率和如:w[n] - (w[j] - w[i+1-1])

        对于在右端点的状态同理。

       初始状态:先全部初始化为一个极大值,对于初始位置c,该点的功耗是0,dp[c][c][0]=dp[c][c][1]=0

参考代码:

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9+7;
int t[51];
int sum[51];
int dp[55][55][2];

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1,w;i<=n;i++)
    {
        scanf("%d%d",&t[i],&w);
        sum[i]=sum[i-1]+w;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            dp[i][j][0]=dp[i][j][1]=inf;
    dp[m][m][0]=dp[m][m][1]=0;
    for(int len=2;len<=n;len++)
    {
        for(int i=1,j=i+len-1;j<=n;i++,j++)
        {
            dp[i][j][0]=min(dp[i+1][j][0]+(t[i+1]-t[i])*(sum[n]-(sum[j]-sum[i])),dp[i+1][j][1]+(t[j]-t[i])*(sum[n]-(sum[j]-sum[i])));
            dp[i][j][1]=min(dp[i][j-1][1]+(t[j]-t[j-1])*(sum[n]-(sum[j-1]-sum[i-1])),dp[i][j-1][0]+(t[j]-t[i])*(sum[n]-(sum[j-1]-sum[i-1])));
        }
    }
    printf("%d\n",min(dp[1][n][0],dp[1][n][1]));
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值