区间DP,就是在区间上做DP(谁都知道)
要搞一个区间,就需要一个双重循环:(写法一大堆,这是其中一种)
for (int len=2;len<=n;len++)//枚举区间长度len for (int i=1;i<=n;i++)//枚举区间左端点 int j=i+len-1;//计算区间右端点
然后balabalaDP就好了
石子合并:
在操场上沿一直线排列着n堆石子。现要将石子有次序地合并成一堆。
规定每次只能选相邻的两堆石子合并成新的一堆,并将新的一堆石子数计为该次合并的得分。 我们希望这n-1次合并后得到的得分总和最小。
这就模板题了,学区间DP必切的一道。
#include <bits/stdc++.h> using namespace std; int n; int f[1000][1000]; int sum[1000]; inline int read() { int c=getchar(),flag=1,sum=0; for (;c<'0' || c>'9';c=getchar()) if (c=='-') flag=-1; for (;c>='0' && c<='9';c=getchar()) sum=(sum<<3)+(sum<<1)+c-48; return sum*flag; } inline void init() { n=read(); for (int i=1;i<=n;i++) { int x=read(); sum[i]=sum[i-1]+x;//前缀和 } } inline void work() { for (int len=1;len<=n;len++) for (int i=1,j=len+1;j<=n;i++,j++) { f[i][j]=1000000007;//保证不会一直选择f[i][j]的决策 for (int k=i;k<j;k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);//sum[j]-sum[i-1]是i-j的区间和 } printf("%d\n",f[1][n]);//最终结果合并到1-n的区间中 } int main() { init(); work(); return 0; }
矩阵连乘:
给定 n个矩阵{A1,A2,...,An},考察这 n个矩阵的连乘积 A1A2...An。由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序,这种计算次序可以用加括号的方式来确定。
矩阵连乘积的计算次序与其计算量有密切关系。例如,考察计算 3 个矩阵{A1,A2,A3}连乘积的例子。 设这3个矩阵的维数分别为10∗100,100∗5,和5∗50 10∗100,100∗5,和5∗50。
若按(A1A2)A3 计算,3 个矩阵连乘积需要的数乘次数为 10∗100∗5+10∗5∗50=7500 10∗100∗5+10∗5∗50=7500。
若按 A1(A2A3)计算,则总共需要100∗5∗50+10∗100∗50=75000 100∗5∗50+10∗100∗50=75000次数乘。
现在你的任务是给出一个矩阵连乘式,计算其需要的最少乘法次数。
这也是基本的套路,根据题意需要把求和改为矩阵乘法,另外还要注意将老维数套到新的矩阵中
#include <bits/stdc++.h> using namespace std; int n; int f[1000][1000]={}; struct hazaking { int h,l; }a[1000][1000]; inline int read() { int c=getchar(),flag=1,sum=0; for (;c<'0' || c>'9';c=getchar()) if (c=='-') flag=-1; for (;c>='0' && c<='9';c=getchar()) sum=(sum<<3)+(sum<<1)+c-48; return sum*flag; } inline void init() { n=read(); for (int i=1;i<=n;i++) { a[i][i].h=read(); a[i][i].l=read(); } } inline void work() { for (int len=1;len<=n;len++) for (int i=1,j=len+1;j<=n;i++,j++) { int mi=100000007; for (int k=i;k<j;k++) if (f[i][k]+f[k+1][j]+a[i][k].h*a[i][k].l*a[k+1][j].l<mi) { mi=f[i][k]+f[k+1][j]+a[i][k].h*a[i][k].l*a[k+1][j].l;//合并 a[i][j].h=a[i][k].h;//新维数 a[i][j].l=a[k+1][j].l;//新维数 } f[i][j]=mi; } printf("%d\n",f[1][n]);//最终结果合并到1-n的区间中 } int main() { init(); work(); return 0; }
能量项链:
在 Mars 星球上,每个 Mars 人都随身佩带着一串能量项链。在项链上有
N N 颗能量珠。能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数。并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记。因为只有这样,通过吸盘(吸盘是 Mars Mars 人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量。如果前一颗能量珠的头标记为 m m,尾标记为 r r,后一颗能量珠的头标记为 r r ,尾标记为 n n,则聚合后释放的能量为 m×r×n m×r×n(Mars 单位),新产生的珠子的头标记为 m m,尾标记为 n n。需要时,Mars 人就用吸盘夹住相邻的两颗珠子,通过聚合得到能量,直到项链上只剩下一颗珠子为止。显然,不同的聚合顺序得到的总能量是不同的,请你设计一个聚合顺序,使一串项链释放出的总能量最大。
例如:设 N=4 N=4,4 4 颗珠子的头标记与尾标记依次为 (2,3)(3,5)(5,10)(10,2) (2,3)(3,5)(5,10)(10,2)。我们用记号 ⊕ ⊕ 表示两颗珠子的聚合操作,(j⊕k) (j⊕k) 表示第 j,k j,k 两颗珠子聚合后所释放的能量。则第 4、1 4、1 两颗珠子聚合后释放的能量为:
这一串项链可以得到最优值的一个聚合顺序所释放的总能量为
这也是基础套路题,比较老的NOIP的DP题,跟矩阵连乘差不多
但由于题目给出的是一条项链(一个环),需要破环成链
#include <bits/stdc++.h> using namespace std; int n; int f[1000][1000]; int a[100000]; int ans=0; inline int read() { int c=getchar(),flag=1,sum=0; for (;c<'0' || c>'9';c=getchar()) if (c=='-') flag=-1; for (;c>='0' && c<='9';c=getchar()) sum=(sum<<3)+(sum<<1)+c-48; return sum*flag; } inline void init() { n=read(); for (int i=1;i<=n;i++) a[i]=read(); } inline void work() { for (int i=n+1;i<=2*n;i++) a[i]=a[i-n];//破环成链 a[0]=a[n];//破环成链 for (int len=1;len<2*n;len++) for (int i=len;i>=1 && i>len-n;i--) { for (int k=i;k<len;k++) { f[i][len]=max(f[i][len],f[i][k]+f[k+1][len]+a[i]*a[k+1]*a[len+1]);//合并 } } for (int i=1;i<=n;i++) ans=max(ans,f[i][n+i-1]);//得到答案 printf("%d\n",ans); } int main() { init(); work(); return 0; }
四边形优化扔掉