Codeforces1101F Trucks and Cities 【滑动窗口】【区间DP】

题目分析:

2500的题目为什么我想了这么久。。。

考虑答案是什么。对于一辆从$s$到$t$的车,它有$k$次加油的机会。可以发现实际上是将$s$到$t$的路径以城市为端点最多划分为最大长度最小的$k+1$段。不难发现这样做一定是最优的。

设计DP状态$f[i][j][k]$表示将第$i$座城市到第$j$座城市的路径划分为$k$段。朴素的DP方程是:

$$f[i][j][k] = min(max(f[i][x][k-1],a[j]-a[x])),(which)x \in [i,j]$$

这个DP是$O(n^4)$的,但我们注意到DP状态有单调性。

换句话说,存在一个城市$y$,使得任意$x<y$满足$max(f[i][x][k-1],a[j]-a[x])=a[j]-a[x]$,任意$x>y$满足$max(f[i][x][k-1],a[j]-a[x])=f[i][x][k-1]$。我们考虑的转移只有两个点是有意义的。

对于相同的$i$,$y$的选取仍然具有单调性,所以利用two pointers移动指针可以做到$O(n^3)$

注意:开三维数组会MLE,我们可以滚动一维(甚至可以滚动两维,心动不如行动,试着写一下吧)。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int maxn = 405;
 5 
 6 int n,m,a[maxn],nxt[maxn];
 7 
 8 struct node{int s,t,oil;};
 9 
10 int f[maxn][maxn][2];
11 vector <node> hh[maxn];
12 
13 void read(){
14     scanf("%d%d",&n,&m);
15     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
16     for(int i=1;i<=m;i++){
17     int s,t,oil,tms;
18     scanf("%d%d%d%d",&s,&t,&oil,&tms);
19     tms = min(tms,t-s-1)+1;
20     hh[tms].push_back((node){s,t,oil});
21     }
22 }
23 
24 void work(){
25     long long ans = 0;
26     for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) f[i][j][1] = a[j]-a[i];
27     for(int k=1;k<=n-1;k++){
28     for(int i=0;i<hh[k].size();i++)
29         ans = max(ans,1ll*f[hh[k][i].s][hh[k][i].t][k&1]*hh[k][i].oil);
30     for(int i=1;i<=n;i++){
31         f[i][i][(k+1)&1] = 0; int pts = i;
32         for(int j=i+1;j<=n;j++){
33         while(f[i][pts][k&1] < a[j]-a[pts])pts++;
34         f[i][j][(k+1)&1] = min(f[i][pts][k&1],a[j]-a[pts-1]);
35         }
36     }
37     }
38     printf("%lld\n",ans);
39 }
40 
41 int main(){
42     read();
43     work();
44     return 0;
45 }

猜你喜欢

转载自www.cnblogs.com/Menhera/p/10277568.html