Educational Codeforces Round 19 F(dp+队列优化)

来源:互联网 发布:蓝月亮 荧光剂 知乎 编辑:程序博客网 时间:2024/06/05 01:18

网址:http://codeforces.com/contest/797/problem/F

显然,要先排序。

我们可以先处理出所有老鼠到所有洞的距离,p[i][j]表示第j只老鼠到第i个洞的距离。由于我们每次求的时候都是一段区间的老鼠到一个洞中,所以我们可以通过求前缀和优化计算。

对于p[i][j],其前缀和为d[i][j],表示前j只老鼠都进入i洞中所需要的花费。在实际运算中,因为我是每更换一个i,就从新求一次d[j],所以d用一维数组存储。

显然,本题我们可以用dp[i][j]表示前i个洞放前n只耗子的最小花费。则dp[i][j] = min{dp[i - 1][k]  + d[i][j] - d[i][k-1]},其中 j - b[i] <= k <= j,(b[i]为第i个洞的容量,此方程就是说枚举进入当前洞的老鼠的数量,找出最优解。

记住!只有形如 dp[i]=max/min (f[k]) + g[i]  k<i && g[i]是与k无关的变量)才能用到单调队列进行优化。

优化的对象就是f[k]


但是,现在我们的复杂度为O(n^2 * m),复杂度过高,故需使用队列优化。

我们注意到,将dp方程进行一些变化后,可以得到dp[i][j] = min{dp[i - 1][k]  - d[i][k - 1]} +d[i][j],显然,min中的部分是一个关于k的函数,并且每次要求的是k在一定范围内的最小值,而且区间范围是单调变化的(区间起点终点一直在增加)。那么我们就可以对这个函数进行队列优化。

设置一个双端队列,保存当前区间的最小值。显然,对于两个点p,q,p < q,若p处的值比q还要大,那么p处的值永远都不可能被使用了,所以就将这个值剔除。每次区间延长时,将新加入的值与原队列末尾的值进行比较,如果新值较小,就将原队尾的的值剔除,再比较,直到不再满足条件,再将新值插入到队列尾端。而每次求当前区间的最小值,就是队列的第一个值。

#include <bits/stdc++.h>#define ll long longusing namespace std;const ll maxn = 5000 + 10;const ll INF = 0x3f3f3f3f3f3f3f3f;ll n,m;ll dp[maxn][maxn];ll d[maxn];deque<ll> deq;struct Hole{ll a;ll b;bool operator <(const Hole & p) const {return a < p.a;}}holes[maxn];ll p[maxn];int main(){scanf("%I64d%I64d",&n,&m);for(int i = 1;i <= n;i++){scanf("%I64d",&p[i]);}for(int i = 1;i <= m;i++) {scanf("%I64d%I64d",&holes[i].a,&holes[i].b);}sort(p + 1,p +n +1);sort(holes + 1,holes + m + 1);memset(dp,127,sizeof(dp));for(int i = 0;i <= m;i++) dp[i][0] = 0;for(int i = 1;i <= m;i++){d[0] = 0;for(int j = 1;j <= n;j++) d[j] = d[j - 1] + llabs(holes[i].a - p[j]);deq.clear();for(int j = 0;j <= n;j++){int temp = j - holes[i].b - 1;if(temp >= 0 && dp[i - 1][temp] - d[temp] == deq.front()) deq.pop_front();while(!deq.empty() && deq.back() > dp[i - 1][j] - d[j]) deq.pop_back();deq.push_back(dp[i- 1][j] - d[j]);dp[i][j] = deq.front() + d[j];}}if(dp[m][n] >= INF) printf("-1\n");else printf("%I64d\n",dp[m][n]);return 0;}


阅读全文
1 0
原创粉丝点击