Making the Grade Page267 线性dp
d p [ i ] dp[i] dp[i]表示处理完前i个的构造,且 b i = a i b_i=a_i bi=ai
(对于非递减的B序列)
d
p
[
i
]
=
m
i
n
{
d
p
[
j
]
+
c
o
s
t
(
i
,
j
)
∣
1
≤
j
≤
i
,
a
[
j
]
≤
a
[
i
]
}
dp[i]=min\{dp[j]+cost(i,j)|1\leq j\leq i,a[j]\leq a[i]\}
dp[i]=min{dp[j]+cost(i,j)∣1≤j≤i,a[j]≤a[i]}
其中,
c
o
s
t
(
i
,
j
)
cost(i,j)
cost(i,j)表示把
[
i
,
j
]
[i,j]
[i,j]区间内的数修改为开头一段
a
i
a_i
ai和结尾一段
a
j
a_j
aj
最后输出的是dp[n],对于那些i, a i ≤ a n a_i\leq a_n ai≤an的,可以由转移方程转移到n,剩下的那些无法转移到n,但是可以通过把 [ i , n ] [i,n] [i,n]这段全部修改 a i a_i ai来转移到dp[n]
由于整体复杂度是O(n^3),所以最后只过了80
超时代码:
int n;
ll dp[maxn],a[maxn],pr[maxn],la[maxn],cnt=INFF;
ll cost(ll l,ll r)
{
pr[l]=la[r+1]=0;//[l,i] (i,r]
rep(i,l+1,r)pr[i]=pr[i-1]+abs(a[i]-a[l]);
for (int i=r;i>=l;i--)la[i]=la[i+1]+abs(a[i]-a[r]);
ll ans=INFF;
rep(i,l,r)ans=min(ans,pr[i]+la[i+1]);
return ans;
}
int main()
{
scanf("%d",&n);
rep(i,1,n)scanf("%lld",&a[i]),dp[i]=INFF;
dp[1]=0;
rep(i,1,n)
{
rep(j,1,i-1)if (a[j]<=a[i])dp[i]=min(dp[i],dp[j]+cost(j,i));
}
rep(i,1,n)
{
if (a[i]<=a[n])continue;
ll j=0;
rep(k,i+1,n)j+=abs(a[k]-a[i]);
cnt=min(cnt,dp[i]+j);
}
WW(min(cnt,dp[n]));
return 0;
}
修改dp状态为dp[i][j]表示处理完前i个的构造,且
b
i
=
j
b_i=j
bi=j,但是j的范围太大, 需要离散化处理
d
p
[
i
]
[
j
]
=
m
i
n
{
d
p
[
i
−
1
]
[
k
]
+
∣
a
i
−
j
∣
}
,
0
≤
k
≤
j
dp[i][j]=min\{dp[i-1][k]+|a_i-j|\},0\leq k\leq j
dp[i][j]=min{dp[i−1][k]+∣ai−j∣},0≤k≤j
外层循环是i,再里一层是j,当i和j确定下来之后,即求
m
i
n
{
d
p
[
i
−
1
]
[
k
]
}
min\{dp[i-1][k]\}
min{dp[i−1][k]},不断更新这个最小值,不需要每次都遍历[0,k],而是用决策集合记录下来,从而降低一个维度
AC代码:
ll n,a[maxn],aa[maxn],b[maxn],dp[maxn][maxn],m=0;
void read()
{
scanf("%d",&n);
rep(i,1,n)scanf("%lld",&a[i]);
}
int query(ll x){return lower_bound(b+1,b+1+m,x)-b;}
void init()
{
rep(i,1,n)aa[i]=a[i];
sort(aa+1,aa+1+n);
rep(i,1,n)if (i==1||aa[i]!=aa[i-1])b[++m]=aa[i];
rep(i,0,n)rep(j,1,n)dp[i][j]=INFF;
dp[1][query(a[1])]=0;
}
void solve()
{
rep(i,1,n)
{
ll val=INF;
rep(j,1,m)
{
val=min(dp[i-1][j],val);
dp[i][j]=min(dp[i][j],val+abs(a[i]-b[j]));
}
}
ll ans=INFF;
rep(j,1,n)ans=min(ans,dp[n][j]);
WW(ans);
}
int main()
{
read();init();solve();
return 0;
}