1. 程式人生 > >關路燈(區間DP)

關路燈(區間DP)

描述

Dr.Kong設計的機器人卡多越來越聰明。最近市政公司交給卡多一項任務,每天早晨5:00開始,它負責關掉ZK大道右側上所有的路燈。

卡多每到早晨5:00準會在ZK大道上某盞路燈的旁邊,然後他開始關燈。每盞燈都有一定的功率,機器人卡多有著自覺的節能意識,它希望在關燈期間,ZK大道右側上所有路燈的耗電量總數是最少的。

機器人卡多以1m/s的速度行走。假設關燈動作不需要花費額外的時間,因為當它通過某盞路燈時就順手將燈關掉。

請你編寫程式,計算在給定路燈設定,燈泡功率以及機器人卡多的起始位置的情況下,卡多關燈期間,ZK大道上所有燈耗費的最小能量。

輸入
有多組測試資料,以EOF為輸入結束的標誌
每組測試資料第一行: N 表示ZK大道右側路燈的數量 (2≤ N ≤ 1000) 
第二行: V 表示機器人卡多開始關燈的路燈號碼。 (1≤V≤N)
接下來的N行中,每行包含兩個用空格隔開的整數D和W,用來描述每盞燈的引數

D表示該路燈與ZK大道起點的距離 (用米為單位來表示),
W表示燈泡的功率,即每秒該燈泡所消耗的能量數。路燈是按順序給定的。
( 0≤D≤1000, 0≤W≤1000 )
輸出
輸出一個整數,即消耗能量之和的最小值。注意結果小於200,000,000
樣例輸入
4 
3
2 2
5 8
6 1
8 7

樣例輸出

56

解題思路:這道題很明顯是用區間dp,可是與以往的區間dp不同,因為對於區間[i,j],機器人所處的位置要麼在i,要麼在j(因為機器人要移動到某一點才能關閉燈泡,所以對於某一段區間來說,機器人最後肯定在兩個端點上,否則將不能成立),那麼既然要表示在左端點還是右端點,所以我們再開三維陣列dp[i][j][0]表示停留在i點,dp[i][j][1]表示停留在j點,那麼剩下的就是狀態方程了,跟普通的區間dp一樣,很容易寫出來。。具體的看程式碼

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>


#define LL long long int
#define min(a,b) (a<b?a:b)


int dp[1005][1005][2];
int dis[1005];
int cost[1005];


const int MAX = 200000005;


int main()
{
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);


int n,p;
while (~scanf("%d", &n))
{
scanf("%d", &p);
scanf("%d %d", &dis[0], &cost[0]);
for (int i = 1; i < n; ++i)
{
scanf("%d %d", &dis[i], &cost[i]);
cost[i] += cost[i - 1];
}
for (int i = 0; i < n; ++i)
{
for (int j = i; j < n; ++j)
{
dp[i][j][0] = dp[i][j][1] = MAX;
}
}
--p;
dp[p][p][0] = dp[p][p][1] = 0;
for (int k = 1; k < n; ++k)
{
for (int i = (p - k) > 0 ? (p - k) : 0; i <= p; ++i)
{
int j = i + k;
if (j >= n)
break;
dp[i][j][0] = min( dp[i + 1][j][1] + (dis[j] - dis[i])*(cost[i] + cost[n - 1] - cost[j])

, dp[i + 1][j][0] + (dis[i + 1] - dis[i])*(cost[i] + cost[n - 1] - cost[j]));
dp[i][j][1] = min( dp[i][j - 1][1] + (dis[j] - dis[j - 1])*(cost[i - 1] + cost[n - 1] - cost[j - 1]), dp[i][j - 1][0] + (dis[j] - dis[i])*(cost[i - 1] + cost[n - 1] - cost[j - 1]));
}
}
printf("%d\n", min(dp[0][n - 1][0], dp[0][n - 1][1]));
}
}