经过昨天一晚上的冥思苦想以及 对四边形不等式的研究,我终于将T2的正解打了出来。
先看题:
就是保证一棵树的中序遍历不变,让你构造一棵树,满足题意。
这道题可以用区间DP,令
f
i
,
j
f_{i,j}
fi,j表示只考虑节点[ i , j ]的答案。
在[i,j]区间中枚举决策点(根节点)k,则有
f
i
,
j
=
m
i
n
(
f
i
,
k
−
1
,
f
k
+
1
,
j
)
+
∑
x
=
i
j
s
u
m
[
x
]
f_{i,j}=min(f_{i,k-1},f_{k+1,j})+\sum_{x=i}^jsum[x]
fi,j=min(fi,k−1,fk+1,j)+∑x=ijsum[x],
其中sum[x]为节点[ i , j ]的权值之和,可以用预处理的 j 的前缀和减去 i 的前缀和得到。
这是O(n
2
^2
2)的做法,可以得40分。
接下来我们就要用到四边形不等式优化(详情可在《算法竞赛进阶指南》上查找)
可以证明
f
i
,
j
f_{i,j}
fi,j满足四边形不等式,则有k单调,所以我们只需在[ i ,j-1 ]与[ i+1 , j ]的决策点之间枚举即可。
下面贴代码:
#include<bits/stdc++.h>
using namespace std;
#define in read()
int n,aa,p[5005][5005];
long long f[5005][5005],a[5005];
int in
{
int i=0;char ch=0;
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) {i=i*10+ch-'0';ch=getchar();}
return i;
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=in;
for(int i=1;i<=n;i++)
{
aa=in;
a[i]=a[i-1]+aa;
}
for(int l=n;l>=1;l--)
for(int r=l;r<=n;r++)
{
f[l][r]=a[r]-a[l-1];
if(l==r) {p[l][r]=l;continue;}
long long minn=1e15;
for(int i=p[l][r-1];i<=p[l+1][r];i++)
{
if(f[l][i-1]+f[i+1][r]<minn)
minn=f[l][i-1]+f[i+1][r],p[l][r]=i;
}
f[l][r]+=minn;
}
printf("%lld",f[1][n]);
return 0;
}