P3080

发布时间 2024-01-07 08:15:04作者: HS_xh

题目传送门

区间DP

\(F_{i,j,0}\)\([i,j]\) 的牛左端的区间最小花费, \(F_{i,j,1}\)\([i,j]\) 的牛右端的区间最小花费。

接着再找到原点 \(O\) ,设为 \(b\) 点,将 \(F_{b,b,0}\)\(F_{b,b,1}\) 设为 \(0\),其余为极大值(0x3f)。

接来下是动态转移方程:

\[f_{i,j,0}=\min(f_{i+1,j,0}+(in_{i+1}-in_{i})\times(n+i-j),f_{i+1,j,1}+(in_{j}-in_{i})\times(n+i-j)) \]

\[f_{i,j,1}=\min(f_{i,j-1,1}+(in_j-in_{j-1})\times(n+i-j),f_{i,j-1,0}+(in_j-in_i)\times(n+i-j)) \]

最后取结果要再次求最小值,求左端和右端的最小值即为结果。

详见代码:

#include<bits/stdc++.h>
using namespace std;
int in[2001],b,n;
int f[2001][2001][2];
int main()
{
	memset(f,0x3f,sizeof(f));
	cin>>n;
	for(int i=1;i<=n;++i)
		cin>>in[i];
	in[++n]=0;//标记原点 
	sort(in+1,in+n+1);
	for(int i=1;i<=n;++i)
		if(in[i]==0) b=i;//找原点 
		f[b][b][0]=f[b][b][1]=0;
	for(int j=b;j<=n;j++)
	   for(int i=j-1;i>=1;i--)
	   {
		  f[i][j][0]=min(f[i+1][j][0]+(in[i+1]-in[i])*(n+i-j),f[i+1][j][1]+(in[j]-in[i])*(n+i-j));
		  f[i][j][1]=min(f[i][j-1][1]+(in[j]-in[j-1])*(n+i-j),f[i][j-1][0]+(in[j]-in[i])*(n+i-j));
	   }
	cout<<min(f[1][n][0],f[1][n][1]);
}