hdu 4960 Another OCD Patient(dp)2014多校训练第9场

来源:互联网 发布:python做时间序列分析 编辑:程序博客网 时间:2024/05/20 18:02

题目链接:hdu4960

题意:给出一些物体的体积,把其中一些物体合并成一个物体(体积为合并前几个物体的体积之和),使得合并后的物体的体积左右对称,即体积是回文的。

分析:由于题目要求把原数列变成回文数列,所以只需要找出原数组中的关键点,然后利用这些关键点进行dp。

所谓的关键点就是指数对(i,j)满足v1+v2+……+vi = vj+……+vn。因为关键点i和j是一一对应的,所以一维dp就能解决问题。

这个关键点可以一开始进行预处理,存储在一个数组里。


dp[i]表示添加第i个关键点后形成回文数列的最小花费

则dp[l]=min(dp[l],dp[i+1]+val[i+1-l]+val[r+1-j]);

,其中i+1-l表示左边第j个关键点到第i个关键点的元素个数的总数,r+1-j表示右边第j个关键点到第i个关键点的元素个数的总数


这边的dp转化的意思其实要么就是整个区间合成一个的价值,要么就是中间一个小区间加上旁边两个相同和区间分别合成一个的价值,两个去价值小的


结束的话,就是l>=r,l>r很好理解,当l==r时就是中间只有1个,将他合成一个的代价也是0.



ac代码:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<queue>using namespace std;#define maxn 5000+10long long  dp[maxn];int n;long long w[maxn];int val[maxn];long long sum[maxn];int match[maxn];void dfs(int l,int r){if(l>=r){dp[l]=0;return ;}if(dp[l]!=-1)return ;int j;dp[l]=val[r-l+1];for(int i=l;i<=n;i++){if(match[i]!=-1&&match[i]<i)break;if(match[i]==-1&&sum[i]>sum[n]/2)break;if(match[i]!=-1){j=match[i];if(i==j)continue;dfs(i+1,j-1);dp[l]=min(dp[l],dp[i+1]+val[i+1-l]+val[r+1-j]);//cout<<l<<"+++"<<dp[l]<<endl;}}}int main(){while(~scanf("%d",&n)&&n){memset(sum,0,sizeof sum);memset(dp,-1,sizeof dp);for(int i=1;i<=n;i++){scanf("%lld",&w[i]);sum[i]=sum[i-1];sum[i]+=w[i];}memset(match,-1,sizeof match);for(int i=1,j=n;i<=j;){if(sum[i]==(sum[n]-sum[j-1])){match[i]=j;i++,j--;}else if(sum[i]>(sum[n]-sum[j-1])){j--;}else{i++;}}//for(int i=1;i<=n;i++)//printf("%d ",match[i]);for(int i=1;i<=n;i++)scanf("%d",&val[i]);dfs(1,n);printf("%lld\n",dp[1]);}return 0;}