P1594 护卫队

题目:

题目坐标:护卫队.

护卫车队在一条单行的街道前排成一队,前面河上是一座单行的桥。因为街道是一条单行道,所以任何车辆都不能超车。桥能承受一个给定的最大承载量。为了控制桥上的交通,桥两边各站一个指挥员。护卫车队被分成几个组,每组中的车辆都能同时通过该桥。当一组车队达到了桥的另一端,该端的指挥员就用电话通知另一端的指挥员,这样下一组车队才能开始通过该桥。每辆车的重量是已知的。任何一组车队的重量之和不能超过桥的最大承重量。被分在同一组的每一辆车都以其最快的速度通过该桥。一组车队通过该桥的时间是用该车队中速度最慢的车通过该桥所需的时间来表示的。问题要求计算出全部护卫车队通过该桥所需的最短时间值。

输入格式:

第一行包含三个整数,第一个整数表示该桥所能承受的最大载重量(用吨表示);第二个整数表示该桥长度(用千米表示);第三个整数表示该护卫队中车辆的总数( n < 1000 n<1000 n<1000)。接下来的几行中,每行包含两个正整数 W 和 S(用空格隔开),W 表示该车的重量(用吨表示),S 表示该车过桥能达到的最快速度(用千米/小时表示)。车子的重量和速度是按车子排队等候时的顺序给出的。

输出格式:

输出文件应该是一个实数,四舍五入精确到小数点后1位,表示整个护卫车队通过该桥所需的最短时间(用分钟表示)。

输入输出样例:

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

题解:

状态:dp[i]表示前i个车,经过合理的组合,所得到的最小时间。所以当每枚举到一个i时,只用往前找在合理的情况下选择最小的一组。
状态转移方程: d p [ i ] = m a x ( d p [ i ] , d p [ j − 1 ] + i — j 的 最 小 值 ) dp[i] = max(dp[i], dp[j - 1] + i—j的最小值) dp[i]=max(dp[i],dp[j1]+ij)
i表示前i辆车, d p [ j − 1 ] + i — j 的 最 小 值 dp[j - 1] + i—j的最小值 dp[j1]+ij 表示前i辆车,将i—j分成一组的时间值。
注意一组的时间值即为每一组最慢车的时间。
还有就是我们可以预先处理一个前i辆车重量的前缀数组,然后每次对于i,j求一个区间重量判断是否合理即可。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAXN = 1005, MAXM = 1e5 + 5;
double zd, w[MAXN], s[MAXN], dp[MAXN], f[MAXN][55], m, d[MAXN];
int n;
double q(int l, int r) {
	double _max = -0x3f3f3f3f;//因为同样路程时间最大的,速度最慢所以每次取max
	for(int i = l;i <= r; i++) _max = max(_max, f[i][0]);
	return _max;
}
int main() {
	scanf("%lf %lf %d", &zd, &m, &n);
	for(int i = 1;i <= n; i++) {
		scanf("%lf %lf", &w[i], &s[i]);
		dp[i] = double(m / s[i] * 1.0);
		f[i][0] = dp[i];
	}
	for(int i = 1;i <= n; i++) d[i] = d[i - 1] + w[i];
	for(int i = 1;i <= n; i++) {
		dp[i] += dp[i - 1];
		for(int j = i - 1;j >= 1; j--) {
			if(d[i] - d[j - 1] <= zd) dp[i] = min(dp[i], dp[j - 1] + q(j, i));
			else break;
		}
	}
	printf("%.1lf", dp[n] * 60);//注意最后求的是分钟数
	return 0; 
} 

如果是求区间最大我们不难想到ST.。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAXN = 1005, MAXM = 1e5 + 5;
double zd, w[MAXN], s[MAXN], dp[MAXN], f[MAXN][55], m, d[MAXN];
int n;
void ST(double n) {
	int k = log(n) / log(2) + 1;
	for(int j = 1;j < k; j++) {
		for(int i = 1;i <= n - (1 << j) + 1; i++) {
			f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
		}
	}
} 
double STF(int l, int r) {
	int k = log(r - l + 1) / log(2);
	return max(f[l][k], f[r - (1 << k) + 1][k]);
}
int main() {
	scanf("%lf %lf %d", &zd, &m, &n);
	for(int i = 1;i <= n; i++) {
		scanf("%lf %lf", &w[i], &s[i]);
		dp[i] = double(m / s[i] * 1.0);
		f[i][0] = dp[i];
	}
	ST(n);
	for(int i = 1;i <= n; i++) d[i] = d[i - 1] + w[i];
	for(int i = 1;i <= n; i++) {
		dp[i] += dp[i - 1];
		for(int j = i - 1;j >= 1; j--) {
			if(d[i] - d[j - 1] <= zd) dp[i] = min(dp[i], dp[j - 1] + STF(j, i));
			else break;
		}
	}
	printf("%.1lf", dp[n] * 60);
	return 0; 
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值