CF1101F Trucks and Cities

一、题目

点此看题

二、解法

可以把加油理解成划段,那么题目转化成求点 [ i , j ] [i,j] [i,j]之间划 k k k段最大的段的最小值是多少,对于所有的车算出这个值乘以 c c c,然后取最大值就是打啊。

d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] [ i , j ] [i,j] [i,j] k k k段的最大段最小值,转移:
d p [ i ] [ j ] [ k ] = max ⁡ ( d p [ i ] [ l ] [ k − 1 ] , t [ j ] − t [ l ] ) dp[i][j][k]=\max(dp[i][l][k-1],t[j]-t[l]) dp[i][j][k]=max(dp[i][l][k1],t[j]t[l])这样做是 O ( n 4 ) O(n^4) O(n4)的,观察式子,发现 d p [ i ] [ l ] [ k − 1 ] dp[i][l][k-1] dp[i][l][k1] l l l单调递增, t [ j ] − t [ l ] t[j]-t[l] t[j]t[l] l l l单调递减,我们可以维护一个边界 l l l,使得 l l l d p [ i ] [ l ] [ k − 1 ] dp[i][l][k-1] dp[i][l][k1]恰好更大, l − 1 l-1 l1 t [ j ] − t [ l ] t[j]-t[l] t[j]t[l]恰好更大,容易发现这是一个指针的移动,所以时间复杂度优化到了 O ( n 3 ) O(n^3) O(n3)

#include 
#include 
#include  
#include 
using namespace std;
const int M = 405;
const int N = 250005;
int read()
{
    int x=0,flag=1;char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int n,m,t[M],a[N],b[N],c[N],d[N],dp[2][M][M];
vector<int> v[M];long long ans;
signed main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)
		t[i]=read();
	for(int i=1;i<=m;i++)
	{
		a[i]=read();b[i]=read();
		c[i]=read();d[i]=read()+1;
		d[i]=min(d[i],b[i]-a[i]);
		v[d[i]].push_back(i);
	}
	memset(dp,0x3f,sizeof dp);
	for(int i=1;i<=n;i++)
		for(int j=i;j<=n;j++)
			dp[1][i][j]=t[j]-t[i];
	for(int k=1;k<n;k++)
	{
		for(int i=0;i<v[k].size();i++)
		{
			int x=v[k][i];
			ans=max(ans,1ll*dp[k&1][a[x]][b[x]]*c[x]);
		}
		for(int i=1;i<=n;i++)
			dp[(k+1)&1][i][i]=0;
		for(int i=1;i<=n;i++)
		{
			int l=i;
			for(int j=i+1;j<=n;j++)
			{
				while(dp[k&1][i][l]<t[j]-t[l]) l++;
				dp[(k+1)&1][i][j]=min(dp[k&1][i][l],t[j]-t[l-1]);
			}
		}
	}
	printf("%lld\n",ans);
}

你可能感兴趣的:(dp)