[HDOJ 4960] Another OCD Patient [乱搞+动态规划]

69 篇文章 0 订阅
26 篇文章 0 订阅

给定一个序列,你可以进行一些操作,每个操作是把序列中相邻的k个数和成一个数,那个数就是这k个数的和,操作的费用就是co[k],问把序列合成一个对称的回文序列所需要的最少花费。任意一个元素只能被合成一次,也就是说,他不会用合成出来的结果再去和其他数合成。

首先处理最左边的元素和最右边的元素,可以发现,如果他们两个值不一样,那么较小的元素一定会和他旁边的元素合并。重复处理,直到两个元素相等,再重复处理除去这两个元素以后剩下的序列。

这样我们就可以得到一个新的序列,这个序列是回文的,并且每个元素都是之前的若干个元素组成的。如果这个序列是偶数个,那么我们在这个序列的正中间插入一个0,认为它是由之前的0个元素组成的,将其转化为一个奇数个元素的序列。

然后我们在考虑合并这个新序列,合并的方法即为左边和右边对称的做相同的操作。注意中间的那块要单独处理。

计算出将任意段合并成一段所需要的费用后,就可以用dp计算出整个的最小费用了。

#include <cstdio>
#include <cstring>

const int INF=~0u>>1;
inline void update(int &a,int b) {
	if (a>b) a=b;
}

int a[5000];
int co[5001];
int lna[2500];
int rna[2500];
int numa[5001];
int cost[2501][2501];
int dp[2501];
int n,newn,m;

void calNewA() {
	int i=0,j=n-1,lnum=0,rnum=0,nn=0;
	long long lsum=0,rsum=0;
	while (i<=j) {
		if (lsum==rsum&&lsum!=0) {
			lna[nn]=lnum;
			rna[nn]=rnum;
			nn++;
			lsum=rsum=0;
			lnum=rnum=0;
		} else if (lsum==rsum&&lsum==0) {
			lsum+=a[i++];
			lnum++;
			rsum+=a[j--];
			rnum++;
		} else if (lsum<rsum) {
			lsum+=a[i++];
			lnum++;
		} else if (lsum>rsum) {
			rsum+=a[j--];
			rnum++;
		}
	}
	if (i==j+1) {
		if (lsum==rsum) {
			lna[nn]=lnum;
			rna[nn]=rnum;
			nn++;
			numa[nn]=0;
		} else {
			numa[nn]=lnum+rnum;
		}
	} else if (i==j+2) {
		numa[nn]=1;
	}
	for (i=0;i<nn;i++) {
		numa[i]=lna[i];
		numa[nn+nn-i]=rna[i];
	}
	m=nn;
	newn=nn+nn+1;
}
void calCost() {
	int i,j;
	for (i=0;i<m;i++) {
		int lnum=lna[i],rnum=rna[i];
		cost[i][i]=co[lnum]+co[rnum];
		for (j=i+1;j<m;j++) {
			lnum+=lna[j];
			rnum+=rna[j];
			cost[i][j]=co[lnum]+co[rnum];
		}
	}
	int num=numa[m];
	cost[m][m]=co[num];
	for (i=m-1;i>=0;i--) {
		num+=lna[i]+rna[i];
		cost[i][m]=co[num];
	}
}

int main() {
	int i,j;
	while (scanf("%d",&n),n) {
		for (i=0;i<n;i++) scanf("%d",&a[i]);
		for (i=1;i<=n;i++) scanf("%d",&co[i]);
		co[0]=co[1]=0;
		calNewA();
		calCost();
		/*
		for (i=0;i<m;i++) {
			printf("%d %d\n",lna[i],rna[i]);
		}
		printf("== %d\n",numa[m]);
		for (i=0;i<=m;i++) {
			for (j=0;j<=m;j++)
				printf("%d ",cost[i][j]);
			printf("\n");
		}
		*/
		dp[0]=cost[0][0];
		for (i=1;i<=m;i++) {
			dp[i]=cost[0][i];
			for (j=0;j<i;j++) update(dp[i],dp[j]+cost[j+1][i]);
		}
		printf("%d\n",dp[m]);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值