【单调队列】【DP】城市交通(jzoj 1749)

城市交通

jzoj 1749

题目大意

有n个点,x到y的前提是x<y,代价是 ( y x ) a x + b y (y-x)*a_x+b_y ,问从1到n的最小代价是多少

输入样例

4
2 9 5 4
9 1 2 2

输出样例

8

数据范围

对于20%的数据, 1 n 100 1\leqslant n\leqslant 100;
对于50%的数据, 1 n 3000 1\leqslant n\leqslant 3000;
对于100%的数据, 1 n 100000 1 A i , B i 1 0 9 1\leqslant n\leqslant 100000,1\leqslant Ai,Bi\leqslant 10^9。

解题思路

y要大于x,就说明不能倒着走,那就可以用DP来做
我们设 f i f_i 为到从1到i的最小代价,如果我们直接枚举从哪里来,那时间复杂度就是 o ( n 2 ) o(n^2) ,那就会TLE
那我们考虑优化
我们可以用单调队列来存有用的状态
有用的状态(i)对比前面所有有用的状态(j)都必须满足以下两个条件之一
1:、 a i < a j a_i<a_j
2、 b i < b j b_i<b_j
这样才可能使答案更优
后面的状态只须从有用的状态转移
当然这样还是可能被卡,但数据太水,A了

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll n, w, p, a[100500], b[100500], f[100500], k[100500];
int main()
{
	scanf("%lld", &n);
	for (ll i = 1; i <= n; ++i)
		scanf("%lld", &a[i]);
	for (ll i = 1; i <= n; ++i)
		scanf("%lld", &b[i]);
	memset(f, 127/3, sizeof(f));
	f[1] = 0;
	k[++w] = 1;//有用的状态
	for (ll i = 2; i <= n; ++i)
	{
		p = 1;
		for (int j = 1; j <= w; ++j)
		{
			f[i] = min(f[i], f[k[j]] + a[k[j]] * (i - k[j]) + b[i]);//转移
			if (a[i] > a[k[j]] && b[i] > b[k[j]]) p = 0;//判断是否是有用的状态
		}
		if (p)
			k[++w] = i;//加入
	}
	printf("%lld", f[n]);
	return 0;
}

注:

这道题还可以用斜率优化做,但本蒟蒻还要学习学习

发布了334 篇原创文章 · 获赞 57 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/ssllyf/article/details/104326035