习题:Trucks and Cities(双指针&DP)

题目

传送门

思路

对于每一辆车,

他的最小容量其实取决于最长的一段路,即我们可以通过求最长路来计算油量

\(dp[i][j][k]\)为从\(i到j\)\(k\)个加油站的最长路的最小值

状态转移比较明显

\(dp[i][j][k]=min\{max\{dp[i][ind][k-1],a[j]-a[ind]\}\}\)

这样的转移明显是\(O(n^4)\)

我们考虑如果固定\(i和j和k\)

那么\(dp[i][ind][k]\)实际上是单增的,\(a[j]-a[ind]\)是单减的

对于\(dp[i][ind][k]>a[j]-a[ind]\)一段

我们一定是取最小的一个\(ind\),使得\(dp[i][ind][k]>a[j][ind]\)

对于\(dp[i][ind][k]<a[j]-a[ind]\)一段

我们一定是取最大的一个\(ind\),使得\(dp[i][ind][k]<a[j]-a[ind]\)

所以实际上我们只需要在这两个\(ind\)之间进行比较即可

如果我们固定\(i\)\(k\),如果\(j\)增大,那么分界点一定也会增大

所以我们可以用一种类似于单调队列的指针来维护

最终的复杂度即为\(O(n^3)\)

代码

#include<iostream>
#include<cstdio>
using namespace std;
int n,m;
int s,f,c,r;
int a[405];
int dp[401][401][401];
long long ans;
/*
区间i~j,设置k个油站
*/
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
            dp[i][j][0]=a[j]-a[i];
    for(int i=1;i<=n;i++)
    {
        for(int k=1;k<=n;k++)
        {
            int _ind=i;
            for(int j=i+1;j<=n;j++)
            {  
            	dp[i][j][k]=dp[i][j][k-1];
                while(dp[i][_ind][k-1]<=a[j]-a[_ind]&&_ind<j)
                    _ind++;
                dp[i][j][k]=min(dp[i][j][k],min(dp[i][_ind][k-1],a[j]-a[_ind-1]));
            }
        }
    }
    for(int i=1;i<=m;i++)
    {
        cin>>s>>f>>c>>r;
        r=min(r,n);
		ans=max(ans,1ll*dp[s][f][r]*c);
    }
    cout<<ans;
    return 0;
}
posted @ 2020-07-29 09:02  loney_s  阅读(87)  评论(0)    收藏  举报