P4072 [SDOI2016] 征途
P4072 [SDOI2016] 征途
化简得:
\[m\sum x_i^2-\left( \sum a_i \right)^2
\]
其实只要最小化 \(\sum x_i^2\),其中 \(x_i\) 为 \(i\) 天的和。
\(80 pts\) 暴力:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e3+10,inf=INT_MAX;
int n,m,a[N];
int dp[N][N]/*前i天前j个*/,sum[N];
int sq(int x){
return x*x;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>a[i],sum[i]=sum[i-1]+a[i];
for(int i=1;i<=m;++i)for(int j=1;j<=n;++j)dp[i][j]=inf;
for(int i=1;i<=n;++i)dp[1][i]=sq(sum[i]);
for(int i=2;i<=m;++i){
for(int j=i;j<=n;++j){
for(int k=max(1ll,i-1);k<j;++k){
dp[i][j]=min(dp[i][j],dp[i-1][k]+sq(sum[j]-sum[k]));
}
}
}
cout<<dp[m][n]*m-sq(sum[n]);
return 0;
}
考虑优化:
\[\begin{equation}
\begin{aligned}
dp_{i,j}&=\min_{k>=i-1,k>=1,j>=i,j>k} dp_{i-1,k}+\left(sum_j-sum_k\right)^2\\
dp_{i-1,k}+\left(sum_j-sum_k\right)^2&=dp_{i-1,k}+sum_j^2+sum_k^2-2\times sum_j \times sum_k\\
\end{aligned}
\end{equation}
\]
只和 \(dp_{i,j}\) 有关的是 \(sum_j^2\),无关紧要 。
只需考虑维护 \(dp_{i-1,k}+sum_k^2-2\times sum_j \times sum_k\) 的最小值。
\(x=sum_j\) , \(t=2\times sum_k\) , \(b=dp_{i-1,k}+sum_k^2\)
我们希望维护最优的 \(k\),对于 \(k<k'\),若 \(k'\) 更优,当且仅当 \(b-tx>b'-t'x\),其中 \(t'-t>0\)
等价于 $ x \textgreater \frac{b'-b}{t'-t} = \lambda $
否则前者更优。
我们要营造一个队列,存储可能的最优决策点,其中队首是当前最优决策点。
\(x\) 递增,\(\lambda_1\) (head与head+1)一旦小于 \(x\) 队首就废了。如果 \(\lambda\) 递减, \(\lambda_2\) (head+1与head+2)也小于 \(x\) 也废了,全废了,只剩下队尾,所以只能维护递增的 \(\lambda\) 。
注意:\(k\in[i-1,j-1]\)。
所以更新前再入队,卡了我半天。(应该是这个问题吧?)
注意:
检查自己单调队列板子有没有写错,比如我犯了点甜甜的错:while -> if ; 多加了个 else。
结束
Code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
const int N=3e3+10,inf=1e18;
ll n,m,a[N];
int dp[N][N]/*前i天前j个*/,sum[N];
int sq(int x){
return x*x;
}
int head,tail,q[N];
double slope(int i,int x,int y){
return (double)(dp[i][x]+sq(sum[x])-dp[i][y]-sq(sum[y]))/(double)(2*sum[x]-2*sum[y]);
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>a[i],sum[i]=sum[i-1]+a[i];
for(int i=0;i<=m;++i)for(int j=0;j<=n;++j)dp[i][j]=inf;
for(int j=1;j<=n;++j) dp[1][j]=sq(sum[j]);
for(int i=2;i<=m;++i){
head=1,tail=0;
q[++tail]=i-1;
for(int j=i;j<=n;++j){
while(head+1<=tail&&slope(i-1,q[head],q[head+1])<sum[j])head++;
int k=q[head];
dp[i][j]=min(dp[i][j],dp[i-1][k]+sq(sum[j]-sum[k]));
while(head+1<=tail&&slope(i-1,q[tail-1],q[tail])>slope(i-1,q[tail],j))tail--;
q[++tail]=j;
}
}
cout<<(ll)(dp[m][n]*m-sq(sum[n]));
return 0;
}

浙公网安备 33010602011771号