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

城市交通

jzoj 1749

题目大意

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

输入样例

4
2 9 5 4
9 1 2 2

输出样例

8

数据范围

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

解题思路

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

代码

#include
#include
#include
#include
#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;
}

注:

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

你可能感兴趣的:(DP)