Vijos-p1355 车队过桥问题(动态规划+ dp)

车队过桥问题

描述

现有N辆车要按顺序通过一个单向的小桥,由于小桥太窄,不能有两辆车并排通过。另外,由于小桥建造的时间已经很久,只能承受有限的重量,记为Max(吨)。管理员将N辆车按初始的顺序分组,每次让一个组过桥,并且只有在一个组的车辆全部过桥后,下一组车辆才能上桥。每辆车的重量和最大速度是已知的,而每组车的过桥时间由该组中速度最慢的那辆车决定。请你帮管理员编一个程序,将这N辆车分组,使得全部车辆通过小桥的时间最短。

格式

输入格式

文件的第一行有3个数字,分别为Max(吨),Len(桥的长度,单位km),N(3个数之间用一个或多个空格隔开)。接下来又N行,每行两个数,第i行的两个数分别表示第i辆车的重量w(吨)和最大速度v(km/h)。

max,len,w,v不超过32位有符号整数类型的最大值,且为整数n < 1000

输出格式

文件只有一行,即全部车辆通过小桥的最短时间(minute),精确到小数点后一位。

样例1

样例输入1

100 5 10
40 25
50 20
50 20
70 10
12 50
9 70
49 30
38 25
27 50
19 70

样例输出1

75.0

限制

1 second

解题

传送门
题目已知按顺序分组,dp[i] 表示前i辆车的最优方案
考虑第i辆车可以与前面相邻k辆车同时过桥分别分析k = (1 ~ i - k + 1)得出最小值就是dp[i]的最优解
dp[i] = min(dp[i], dp[j - 1] + a[j, i])
a[j, i]表示第j至i辆车同时过桥的时间花费(可预处理计算)

代码

#include <algorithm>  //车队过桥问题
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 1002;
int Max, N;
double Len;
int w[maxn], v[maxn];
double dp[maxn];
int minv[maxn * 4];  //线段树, 区间最小值

// 复习线段树
void build(int s, int t, int p) {
    
      //对区间[s, t]建立线段树当前根编号为p
    if (s == t) {
    
    
        minv[p] = v[s];
        return;
    }
    int m = (s + t) / 2;
    build(s, m, p * 2), build(m + 1, t, p * 2 + 1);
    minv[p] = min(minv[p * 2], minv[p * 2 + 1]);
}

int check(int l, int r, int s, int t, int p) {
    
      //查询区间[l, r]的线段树
    if (l <= s && t <= r) return minv[p];
    int m = (s + t) / 2;  //二分是分树, 而不是分查找区间
    if (r <= m) {
    
    
        return check(l, r, s, m, p * 2);
    } else if (m < l) {
    
    
        return check(l, r, m + 1, t, p * 2 + 1);
    } else {
    
    
        return min(check(l, m, s, m, p * 2),
                   check(m + 1, r, m + 1, t, p * 2 + 1));
    }
}

int main() {
    
    
    cin >> Max >> Len >> N;
    for (int i = 1; i <= N; i++) cin >> w[i] >> v[i];
    memset(minv, 0x3f, sizeof(minv));
    build(1, N, 1);

    memset(dp, 0x7f, sizeof(dp));
    dp[0] = 0;
    dp[1] = Len / v[1];

    for (int i = 2; i <= N; i++) {
    
    
        for (int j = i; j >= 1; j--) {
    
      //把区间[j, i]当做一个小队
            long long tot = 0;
            for (int k = j; k <= i; k++) tot += w[k];  //计算区间[j, i]的重量
            if (tot > Max) break;  //如果超重, 以i为最后的计算完毕
            dp[i] = min(dp[i], dp[j - 1] + Len / check(j, i, 1, N, 1));
        }
    }

    printf("%.1f\n", dp[N] * 60);

    system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45349225/article/details/109570310