vijos1451(区间dp,好题,必须回顾进行再分析!!!)

呜呜呜,我真的太水了。


没有人说过,只要是环形的题,就要把它复制一下接在后面。然而我。。。。就犯了这米杀的错误。


这道题

关建1:分析题目的性质,找出有用的信息,即实质所要求的东西

关键2:如果按着题目的描述,来dp的话很难表示,所以通过互补的思维,另一向的思维来做

rmq的模版,以前做这个rmq跪过,最后发现是括号加错了位置!!!int后面的括号要把整个除法都扩住!!,以前没有加括号,就误以为光分子啊!!

k=(int)(log(double(r-l+1))/log(2.0)))

这道题总说:非常锻炼对题目性质与实质的分析

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll inf=10000000000;
ll f[2005][2005][2];
ll mi[2005][20];
int n,m;
ll a[2005];

void init()
{
	for (int i=1;i<=n;i++) mi[i][0]=a[i];
	for (int k=1;k<=15;k++)
	{
		for (int i=1;i<=n;i++)
		if (i+(1<<k)-1<=n) 
		mi[i][k]=max(mi[i][k-1],mi[i+(1<<(k-1))][k-1]);
		else break;
	}
}
ll MIN(int l,int r)
{
	if (l>r) return 0;
	//如果没这个区间的话,就返回费用为0,和下面相呼应!
	//对应了取到最后,就没有费用的情况,因为问题本身,下面的+m,
	//就是保证了要取数的区间,在增加的过程中,区间逐渐变小,
	//到后面为0的时候,因为为0,也不会影响答案 
	int k=(int)(log(double(r-l+1))/log(2.0));
	return max(mi[l][k],mi[r-(1<<k)+1][k]);
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
	f[0][0][0]=0;
	f[0][0][1]=0;
	init();
	
	for (int i=0;i<=n;i++)
	{
		for (int j=0;j<=n-i;j++)
		{
			if (i==0&&j==0) continue;
			int mis=MIN(1+m+i,n-m-j);
			//因为减m,就保证了最后取的那些不会增加费用,和上面相呼应 
			if (i>0) f[i][j][0]=min(f[i-1][j][0]+mis,f[i-1][j][1]+mis*(i+j));
			else f[i][j][0]=inf;
			mis=MIN(1+m+i+1,n-m-j+1);
			if (j>0) f[i][j][1]=min(f[i][j-1][1]+mis,f[i][j-1][0]+mis*(i+j));
			else f[i][j][1]=inf;
		}
	}
	ll tmp=inf;
	for (int i=0;i<=n;i++) tmp=min(tmp,min(f[i][n-i][0],f[i][n-i][1]));
	for (int i=1;i<=n;i++) tmp+=a[i];
	printf("%lld",tmp);
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值