【洛谷P6563】一直在你身旁
作者:互联网
题目
题目链接:https://www.luogu.com.cn/problem/P6563
回到这座小镇后,她的新工作是维修电线。
现在,有一根电线坏了。已知电线长度可能为 \(1\),\(2\),...,\(n\) 中的一个数。现在,她需要知道电线的长度。
她可以花费 \(a_i\) 块钱购买长度为 \(i\) 的电线。购买这根电线后,她能知道所需要的电线长度是否 大于 \(i\)。
保证 \(a_1 \le a_2 \le \cdots \le a_n \le 10^9\)。
问她至少要花多少钱才能保证知道需要电线的长度。
\(Q\leq 500,\sum n\leq 7100\)。
思路
容易想到设 \(f[i][j]\) 表示电线长度在区间 \([i,j]\) 时,至少需要多少钱才能知道电线的长度。
有转移
时间复杂度 \(O(n^3)\)。
我们发现转移中有一个 \(\max\) 并不好处理,所以考虑分类把 \(\max\) 拆开。
对于一个区间 \([i,j]\),我们设 \(k\) 表示满足 \(f[i][k]>f[k+1][j]\) 的最小的位置。不难当 \(i\) 不变时 \(k\) 是单调不减的。所以可以在枚举区间的时候顺便求出。
考虑转移点的位置 \(l\):
- 如果 \(l\geq k\),也就意味着转移式为 \(\min(f[i][l]+a_l)\),显然 \(l\) 越大时价格越大,所以直接取 \(l=k\) 即可。
- 如果 \(l<k\),那么转移式为 \(\min(f[l+1][j]+a_l)\),这个东西虽然不单调,但是我们现在等价于求在 \([i,k)\) 区间内的一个最小值,且 \(k\) 是不断减小的。所以我们可以先枚举 \(j\) 再枚举 \(i\),然后单调队列维护一下即可。
时间复杂度 \(O(n^2)\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=7110;
int Q,n,a[N];
ll f[N][N];
deque<int> q;
int main()
{
scanf("%d",&Q);
while (Q--)
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
for (int j=1;j<=n;j++)
{
while (q.size()) q.pop_back();
for (int i=j-1,k=j-1;i>=1;i--)
{
while (q.size() && f[i+1][j]+a[i]<=f[q.back()+1][j]+a[q.back()]) q.pop_back();
q.push_back(i);
while (k>i && f[i][k-1]>f[k][j]) k--;
f[i][j]=f[i][k]+a[k];
while (q.size() && q.front()>=k) q.pop_front();
if (q.size()) f[i][j]=min(f[i][j],f[q.front()+1][j]+a[q.front()]);
}
}
printf("%lld\n",f[1][n]);
}
return 0;
}
标签:身旁,le,洛谷,int,电线,--,front,长度,P6563 来源: https://www.cnblogs.com/stoorz/p/14729524.html