【post】解题报告

IOI2000啊,比NOIIP还简单。。


用四边形不等式优化了半天都没写对,最后优化了点点系数。

很简单的题。

f[i][j]表示前i个村庄,总共建j个邮局的最小距离和。

预处理可以维护[p,q]区间建一个邮局的最小费用。

用中位数可以用O(n)维护出来(我用前缀和写的o(n^2)的,但是关系不大)

要注意就是如果数量为偶数,中位数会取中间的某一个。


f[i][j] = min(f[k][j-1]+w[k+1][i])

(f[i][0] = maxlong)

k=s[i-1][j-1]就是那个没写完整的四边形不等式优化


后来听说是要改变拓扑序,按区间动规来。前i个邮局是左边界,前j个村庄是右边界。然后就可以用四边形不等式了。

至今未调出来。。


//#include <iostream>
//using std::cout;
//using std::cin;
#define debug(a) printf("%ld",a);
#define debugc(c) printf("%c",c);
#include <cstdio>
const long oo = 0x7fff0000;


long n;long m;
long val[500];
long sum[500];
long w[500][500];
long f[500][50];
long g[500][50];


long s[500][500];


void output(long p,long q)
{
	if (p==0)
	{
		return;
	}
	output(g[p][q],q-1);
	printf("%ld ",p);
}
int main()
{
	freopen("post.in","r",stdin);
	freopen("post.out","w",stdout);
	
	scanf("%ld%ld",&n,&m);
	for (long i=1;i<n+1;i++)
	{
		scanf("%ld",val+i);
		sum[i] = sum[i-1]+val[i];
	}
	
	for (long i=1;i<n+1;i++)
	{
		for (long j=i;j<n+1;j++)
		{
			long t = (i+j)>>1;
			w[i][j] = (sum[j]-sum[t])-(sum[t-1]-sum[i-1])+((t-i)-(j-t))*val[t];
			//s[i][j] = t;
			if (((j-i+1)&1)==0)
			{
				t = (i+j+1)>>1;
				if (w[i][j]>(sum[j]-sum[t])-(sum[t-1]-sum[i-1])+((t-i)-(j-t))*val[t])
				{
					w[i][j] = (sum[j]-sum[t])-(sum[t-1]-sum[i-1])+((t-i)-(j-t))*val[t];
					//s[i][j] = t;
				}
			}
		}
	}
	f[0][0] = 0;
	f[1][1] = 0;
	for (long i=1;i<n+1;i++)
	{
		f[i][0] = oo;
	}
	for (long i=1;i<n+1;i++)
	{
		for (long j=1;j<m+1;j++)
		{
			f[i][j] = oo;
			
			for (long k=s[i-1][j-1];k<=n&&k<i;k++)
			{
				if (f[k][j-1]<oo&&f[i][j] > f[k][j-1]+w[k+1][i])
				{
					s[i][j] = k;
					f[i][j] = f[k][j-1]+w[k+1][i];
					//g[i][j] = k;
				}
			}
		}
	}
	printf("%ld",f[n][m]);
	//output(n,m);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值