方程
$\Large f(i,j)=min(f(i-1,k)+w(k+1,j))$
其中$w(i,j)$表示在$[i,j]$的村庄都去一个邮局的最小距离和
证明w满足四边形不等式
设$w_k(i,j)$表示$[i,j]$的村庄都去$k$村庄邮局的距离和
对于$\forall k$满足$w_k(i,j+1)=w(i,j+1)$都有$w_k(i+1,j)=w(i+1,j)$
(因为k只有选$i,j$的中间位置$w(i,j)$才是最小的)
$w(i,j)+w(i+1,j+1)\le w_k(i,j)+w_k(i+1,j+1)=w(i,j+1)+w(i+1,j)$
证明f满足四边形不等式
设$f_k(i,j)=f(i-1,k-1)+w(k,j)$
对于$\forall i\le i^{'}\le j\le j^{'}$,设$k=s(i,j^{'}),t=s(i^{'},j)$
1.如果$k\le t$
有$f(i,j)+f(i^{'},j^{'})\le f_k(i,j)+f_t(i^{'},j^{'})= f(i-1,k-1)+w(k,j)+f(i^{'}-1,t-1)+w(t,j^{'})$
因为w满足四边形不等式$f(i,j)+f(i^{'},j^{'})\le f(i-1,k-1)+w(k,j^{'})+f(i^{'}-1,t-1)+w(t,j)$
即$f(i,j)+f(i^{'},j^{'})\le f(i,j^{'})+f(i^{'},j)$
2.如果$k\gt t$
则只需证$f(i-1,t-1)+w(t,j)+f(i^{'}-1,k-1)+w(k,j^{'})\le f(i-1,k-1)+w(k,j^{'})+f(i^{'}-1,t-1)+w(t,j)$
设$k_1=s(i-1,k-1),k_2=s(i-2,k_1-1)……k_n=s(i-n,k_{n-1}-1)$
$t_1=s(i^{'}-1,t-1),t_2=s(i^{'}-2,t_1-1)……t_n=s(i^{'}-n,t_{n-1}-1)$
如果$k_1\le t_1$,就可以用上面的证明方法证出来
否则,递归本次证明直到得到求证$f(1,t_n-1)+f(i_{'}-i+1,k_n-1)\le f(1,k_n-1)+f(i_{'}-i+1,t_n-1)$
化简得$w(1,t_n-1)+f(i_{'}-i,t_{n+1}-1)+w(t_{n+1},k_n-1)\le w(1,k_n-1)+f(i_{'}-i,t_{n+1}-1)+w(t_{n+1},t_n-1)$
由w满足四边形不等式得$w(1,t_n-1)+w(t_{n+1},k_n-1)\le w(1,k_n-1)+w(t_{n+1},t_n-1)$
所以$f(i,j)+f(i^{'},j^{'})\le f(i,j^{'})+f(i^{'},j)$
证明$f(i,j)$的决策$s(i,j)$是单调的
1.设$k=s(i,j)$,对于所有$t\le k$
有$w(t,j)+w(k,j+1)\le w(t,j+1)+w(k,j)$
两边同时加上$f(i,t-1)+f(i,k-1)$得$f_t(i,j)+f_k(i,j+1)\le f_k(i,j)+f_t(i,j+1)$
因为$f_t(i,j)\ge f_k(i,j)$,所以$f_k(i,j+1)\le f_t(i,j+1)$
所以$s(i,j)\le s(i,j+1)$
2.设$k=s(i,j)$,对于所有$t\le k$
有$f(i,t-1)+f(i+1,k-1)\le f(i+1,t-1)+f(i,k-1)$
两边同时加上$w(t,j)+w(k,j)$得$f_t(i,j)+f_k(i+1,j)\le f_k(i,j)+f_t(i+1,j)$
因为$f_t(i,j)\ge f_k(i,j)$,所以$f_k(i+1,j\le f_t(i+1,j)$
所以$s(i,j)\le s(i+1,j)$
代码
#include#include using namespace std; #define maxn 305 #define maxp 40 int f[maxp][maxn],s[maxp][maxn],w[maxn][maxn],a[maxn]; int main(){ memset(f,0x3f,sizeof(f)); int n,p;scanf("%d%d",&n,&p); for(int i=1;i<=n;i++)scanf("%d",a+i); for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ w[i][j]=w[i][j-1]+a[j]-a[(i+j)/2]; } s[0][i]=1; } f[0][0]=0; for(int i=1;i<=p;i++){ f[i][i]=0;s[i][i]=i;s[i][n+1]=n; for(int j=n;j>i;j--){ for(int k=s[i-1][j];k<=s[i][j+1];k++){ if(f[i][j]>f[i-1][k-1]+w[k][j]){ f[i][j]=f[i-1][k-1]+w[k][j];s[i][j]=k; } } } } printf("%d",f[p][n]); return 0; }