以石子合并为例的区间DP
区间DP,是一类具有较为固定解法的DP,一般的思路都是:
first.初始化区间长度为1的情况(一般区间长度为1的较易于初始化)
second.
for(枚举区间长度2~n){ for(枚举左端点){ j=i+len-1//记录右端点 for(枚举断点){ //枚举断点后一般是比较以哪个断点分开最优(一般是比较最大或最小) } } }
end.区间DP的特点:
合并:即将两个或多个部分进行整合,当然也可以反过来,也就是对一个问题分解成两个或多个部分。
特征:能将问题分解为两两合并的形式;
求解:对整个问题设最优值,枚举合并点,将问题分解成左右两部分,最后合并左右两个部分的最优值得到原问题的最优值。(显然无后效性啦)
然后我发现区间DP的数据范围都超级小,100左右
下面我萌以石子合并为例子来看具体的看区间DP:
首先要说的是:这道题贪心是不对哒;
然后看正解:区间DP
如果第i堆石子与第j堆石子合并成一堆,说明i~j之间的所有石子也都被合并成了一堆,然后对于合并第i堆石子与第j堆石子的代价,可以看做是先将第i~k堆石子合并,再将第k+1~j堆石子合并的代价别忘记再加上Σ(k=i~j)val[k](val[i]表示第i堆石子的个数,显然不管你合并啥,只要合并第i堆石子~第j堆石子,一定需要加上第i堆石子到第j堆石子的和(感性理解一下qwq)),然后需要求最优的话,就是枚举i~j之间每一个断点k,取最优。
定义:sum[i]表示Σ(k=1~i)val[k](运用了前缀和的思想,如果要求区间i~j的和,可以用sum[j]-sum[i-1])
f_min[i][j]表示合并区间i~j所需要的最小花费;
f_max[i][j]表示合并区间i~j所需要的最大花费;
初始条件:f_min[i][i]=f_max[i][i]=0;(只合并自己一堆显然需要花费为0)
转移方程:
f_min[i][j]=min(f_min[i][j],f_min[i][k]+f_min[k+1][j])+sum[j]-sum[i-1];
f_max[i][j]=max(f_max[i][j],f_max[i][k]+f_max[k+1][j])+sum[j]-sum[i-1];
(i<=k<=j)
显然这个题需要枚举区间长度一层for,枚举区间起点一层for,枚举断点k一层for,然后就是三层for,时间复杂度O(n^3);
然后是对于环的处理,最好写好用的处理方法,把序列延长为原来的两倍。然后最后枚举一下取最优值就好了。
CODE:
#include<bits/stdc++.h> #define ll long long #define INF 2147483647 using namespace std; inline int read(){ int ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } int n,val[202]; int f_max[202][202],f_min[202][202],sum[202]; int main(){ n=read(); for(int i=1;i<=n;i++) val[i]=read(),val[i+n]=val[i]; memset(f_min,0x3f,sizeof(f_min)); for(int i=1;i<=2*n;i++){ sum[i]=sum[i-1]+val[i]; f_min[i][i]=0;f_max[i][i]=0; } for(int len=2;len<=n;len++){ for(int i=1;i+len-1<=2*n;i++){ int j=i+len-1; for(int k=i;k<=j;k++){ f_min[i][j]=min(f_min[i][j],f_min[i][k]+f_min[k+1][j]); f_max[i][j]=max(f_max[i][j],f_max[i][k]+f_max[k+1][j]); } f_min[i][j]+=(sum[j]-sum[i-1]); f_max[i][j]+=(sum[j]-sum[i-1]); } } int ans_min=INF,ans_max=0; for(int i=1;i<=n;i++){ ans_min=min(ans_min,f_min[i][i+n-1]); ans_max=max(ans_max,f_max[i][i+n-1]); } cout<<ans_min<<endl<<ans_max<<endl; return 0; }
跑去做题(逃
end-