• 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 imes len_{i - 1, i}}{v} ]

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

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

    [dp_{i, j, 1} leftarrow dp_{i, j + 1, 1} + frac{cost imes 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
    ", (int)floor(ans + min(dp[pos][pos][0], dp[pos][pos][1])));
    	}
    	return 0;
    }
    
  • 相关阅读:
    《手把手教你构建自己的 Linux 系统》学习笔记(7)
    《手把手教你构建自己的 Linux 系统》学习笔记(6)
    《手把手教你构建自己的 Linux 系统》学习笔记(5)
    《手把手教你构建自己的 Linux 系统》学习笔记(4)
    《手把手教你构建自己的 Linux 系统》学习笔记(3)
    《手把手教你构建自己的 Linux 系统》学习笔记(2)
    《手把手教你构建自己的 Linux 系统》学习笔记(1)
    Mac 下如何判断 WIFI 的极限传输速度还有信号强度?
    Mac 下如何快速重启 Dock 栏?
    编译器的工作过程
  • 原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13992424.html
Copyright © 2020-2023  润新知