车队过桥问题
描述
现有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;
}