快餐店(IOI2000邮局)

快餐店
题解:
显然这道题我们可以将n个加油站分为k段,那么我们可以采用分段dp,用dp[i][j]表示将i个加油站分为j段的最小距离,即可得出转移方程:

dp[i][parts]=min(dp[i][parts],dp[j][parts-1]+dis[j+1][i])

而这里我们需要预处理计算出i到j这段区间的最小距离,应当为中位数所在加油站
IOI2020:没想到我竟然找到了原题!!!
但显然,gg出的题数据范围较小,正常dp就能过,但IOI中数据给到了3000,显然超过了时间限制,所以要用四边形不等式来优化一下 (虽然我也不会)
四边形不等式:https://www.cnblogs.com/luoshuitianyi/p/10354956.html
代码:

#include
using namespace std;
int n,k,a[3005],dp[3005][3005],dis[3005][3005];
int main(){
     
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	memset(dp,0x7f7f7f,sizeof(dp));
	for(int i=1;i<=n;i++){
     
		for(int j=1;j<=n;j++){
     
			int mid=(i+j)>>1;
			for(int k=i;k<=j;k++) dis[i][j]+=abs(a[mid]-a[k]);
		}
		dp[i][1]=dis[1][i];
	}
	for(int parts=2;parts<=k;parts++){
     
		for(int i=parts;i<=n;i++)	
			for(int j=parts-1;j<=i-1;j++) dp[i][parts]=min(dp[i][parts],dp[j][parts-1]+dis[j+1][i]);
	}
	printf("%d",dp[n][k]);
	return 0;
}
/*IOI2020邮局 
四边形不等式(?)优化 
#include
#include
#include
#include
#include
#define gc getchar()
using namespace std;
const int P=305,N=3005;
inline void qr(int &x)
{
	x=0;char c=gc;int f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
	x*=f;
}
void qw(int x)
{
	if(x<0)x=-x,putchar('-');
	if(x/10)qw(x/10);
	putchar(x%10+48);
}
int f[N][P],s[N],a[N],p[N][P];
inline int calc(int l,int r)
{
	int mid=l+r+1>>1;
	return a[mid]*(mid-l+1)-(s[mid]-s[l-1])+(s[r]-s[mid])-a[mid]*(r-mid);
}
int main()
{
	int n,m;qr(n),qr(m);
	for(int i=1;i<=n;i++)qr(a[i]),s[i]=s[i-1]+a[i];
	memset(f,0x3f,sizeof(f));
	for(int i=1;i<=n;i++)f[i][1]=calc(1,i);
	for(int i=1;i<=m;i++)p[n][i]=1;
	for(int j=2;j<=m;j++)
	{
		p[n+1][j]=n;int tmp=0x3f3f3f3f;
		for(int i=n;i>=1;i--)
			for(int k=p[i][j-1];k<=p[i+1][j];k++)
				if((tmp=f[k][j-1]+calc(k+1,i))

你可能感兴趣的:(c++,动态规划)