1. 程式人生 > 實用技巧 >UVA1336 修繕長城 Fixing the Great Wall

UVA1336 修繕長城 Fixing the Great Wall

Description

傳送門


Solution

因為只要經過就會修所以最優的修繕策略是從起點開始往左或往右修一段區間,所以考慮反向區間\(dp\)

\(dp_{i, j}\)表示修繕完區間\([i, j]\)後的時間內再修其它區間產生的花費。

那麼轉移時不需要考慮區間\([i, j]\),只需要考慮除了區間\([i, j]\)以外的點。

發現修繕區間\([i, j]\)時花費的時間會對以後要修復的點產生影響,具體的,還沒有修復的那些點對答案的貢獻會加上從區間\([i, j]\)轉移到下個區間的時間乘單位時間的代價。

所以只需計算區間和區間轉移時花費的時間。

注意到修繕完一個區間後一定是在區間的左/右端點,所以可以設\(dp_{i, j, 0/1}\)

表示當前修繕到區間\([i, j]\)位於左/右端點,以後的時間內修繕完其他的點的代價。

\(s_i\)表示區間\([1, i]\)內的\(\Delta\)的和,\(len_{i, j}\)表示點\(i\)到點\(j\)的距離。很容易的列出\(dp\)式子:

\[cost = s_n - (s_j - s_{i - 1}) \]

\[dp_{i, j, 0} \leftarrow dp_{i - 1, j, 0} + \frac{cost \times len_{i - 1, i}}{v} \]

\[dp_{i, j, 0} \leftarrow dp_{i, j + 1, 1} + \frac{cost \times len_{i, j + 1}}{v} \]

\[dp_{i, j, 1} \leftarrow dp_{i - 1, j, 0} + \frac{cost \times len_{i - 1, j}}{v} \]

\[dp_{i, j, 1} \leftarrow dp_{i, j + 1, 1} + \frac{cost \times len_{j, j + 1}}{v} \]


Code

/*
    _______                       ________                        _______
   / _____ \                     / ______ \                      / _____ \
  / /     \_\  _     __     _   / /      \ \   _     __     _   / /     \_\
 | |          | |   |  |   | | | |        | | | |   |  |   | | | |
 | |          | |   |  |   | | | |     __ | | | |   |  |   | | | |
 | |       __ \  \  |  |  /  / | |     \ \| | \  \  |  |  /  / | |       __
  \ \_____/ /  \  \/ /\ \/  /   \ \_____\  /   \  \/ /\ \/  /   \ \_____/ /
   \_______/    \___/  \___/     \______/\__\   \___/  \___/     \_______/
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

#define db double

const int N = 1000;

int n, x;

db dp[N + 50][N + 50][2], sum[N + 50], ans, v;

struct Point {
	int pos; db zhi, delta;
} point[N + 50];

bool Cmp(Point a, Point b) {
	return a.pos < b.pos;
}

int main() {
	while (scanf("%d%lf%d", &n, &v, &x) == 3) {
		if (!n && !v && !x) return 0;
		for (int i = 1; i <= n; i++) 
			scanf("%d%lf%lf", &point[i].pos, &point[i].zhi, &point[i].delta);
		point[++n].pos = x; point[n].zhi = point[n].delta = 0.0;
		sort(point + 1, point + n + 1, Cmp);
		int pos;
		for (int i = 1; i <= n; i++) 
			if (point[i].pos == x && point[i].delta == 0.0 && point[i].zhi == 0.0) {
				pos = i;
				break;
			}
		ans = 0;
		for (int i = 1; i <= n; i++) 
			sum[i] = sum[i - 1] + point[i].delta,
			ans += point[i].zhi;
		for (int i = 1; i <= n; i++) 
			for (int j = 1; j <= n; j++)
				dp[i][j][0] = dp[i][j][1] = 1e9;
		dp[1][n][0] = dp[1][n][1] = 0.0; 
		for (int l = n - 1; l >= 1; l--)	
			for (int i = 1; i + l - 1 <= n; i++) {
				int j = i + l - 1; db cost = sum[n] - sum[j] + sum[i - 1];
				if (i - 1 >= 1) dp[i][j][0] = min(dp[i][j][0], dp[i - 1][j][0] + cost * (db)(point[i].pos - point[i - 1].pos) / v);
				if (j + 1 <= n) dp[i][j][0] = min(dp[i][j][0], dp[i][j + 1][1] + cost * (db)(point[j + 1].pos - point[i].pos) / v);
				if (j + 1 <= n) dp[i][j][1] = min(dp[i][j][1], dp[i][j + 1][1] + cost * (db)(point[j + 1].pos - point[j].pos) / v);
				if (i - 1 >= 1) dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j][0] + cost * (db)(point[j].pos - point[i - 1].pos) / v);
			}
		printf("%d\n", (int)floor(ans + min(dp[pos][pos][0], dp[pos][pos][1])));
	}
	return 0;
}