P4959 题解
提供一种复杂度更优的做法。
首先可以观察到可以另
考虑一对偏序
称以原点为左下角
考虑剩下的点有什么性质。若按
不然,比如
于是我们可以在排序后的点上 dp,设
则可得到转移方程
#define pi pair<int,int>
#define fi first
#define se second
#define mp make_pair
typedef long long ll;
const int MAXN=5e3+5,INF=0x3f3f3f3f;
int n;pi a[MAXN];
ll dp[MAXN];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){scanf("%d%d",&a[i].fi,&a[i].se);a[i].fi=abs(a[i].fi);a[i].se=abs(a[i].se);}
sort(a+1,a+n+1,greater<pi>());// 先按 x,y 降序排序
int mxy=0,tot=0;
for(int i=1;i<=n;i++) // 若 x 比该点大且 y 不比该点小,则该点被偏序
if(a[i].se>mxy){a[++tot]=a[i];mxy=a[i].se;}
n=tot;for(int i=1;i<=n>>1;i++)swap(a[i],a[n-i+1]);// 之前为了方便将 x 降序,这里反转一下
memset(dp,0x3f,n+1<<3);dp[0]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++)
dp[i]=min(dp[i],dp[j]+a[i].fi*1ll*a[j+1].se);
printf("%lld\n",dp[n]<<2); // 最后乘 4
return 0;
}
然后你发现状态其实是
这个转移的形式特别好,典型的斜优,而且
复杂度
#define pi pair<int,int>
#define fi first
#define se second
#define mp make_pair
typedef long long ll;typedef double db;
const int MAXN=5e3+5,INF=0x3f3f3f3f;
int n;pi a[MAXN];
ll dp[MAXN];
inline ll X(int i){return a[i+1].se;}inline ll Y(int i){return-dp[i];}
inline db K(int i,int j){return(Y(j)-Y(i))*1.0/(X(j)-X(i));}
int l,r,q[MAXN];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){scanf("%d%d",&a[i].fi,&a[i].se);a[i].fi=abs(a[i].fi);a[i].se=abs(a[i].se);}
sort(a+1,a+n+1,greater<pi>());
int mxy=0,tot=0;
for(int i=1;i<=n;i++)
if(a[i].se>mxy){a[++tot]=a[i];mxy=a[i].se;}
n=tot;for(int i=1;i<=n>>1;i++)swap(a[i],a[n-i+1]);
dp[q[l=r=1]=0]=0;
for(int i=1;i<=n;i++){
while(l<r&&K(q[l],q[l+1])<a[i].fi)l++;
dp[i]=dp[q[l]]+a[i].fi*1ll*a[q[l]+1].se;
while(l<r&&K(i,q[r])<K(q[r],q[r-1]))r--;
q[++r]=i;
}
printf("%lld\n",dp[n]<<2);
return 0;
}