区间dp
求区间最优解,将区间分隔为更小的区间,再由小区间最优解得到大区间最优解
模板
for(int len=1;len<=n;len++) //先枚举长度
{
for(int i=1;i+len-1<=n;i++) //枚举起点
{
int j=i+len-1; //由起点和长度得出终点
for(int k=i;k+1<=j;k++)
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+______);
}
}
线性
石子合并
#include<bits/stdc++.h>
using namespace std;
const int N=310;
int a[N],b[N],dp[N][N];
int n;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=b[i-1]+a[i];//记录前缀和
}
memset(dp,0x3f3f3f3f,sizeof(dp));//初始化
for(int len=1;len<=n;len++)
{
for(int i=1;i+len-1<=n;i++)
{
int j=len+i-1;
if(len==1)
{
dp[i][j]=0;//最小区间
continue;
}
for(int k=i;k<=j-1;k++)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+b[j]-b[i-1]);
}
}
printf("%d",dp[1][n]);
}
环状
思路:将链打开
只需要将前n-1个数复制到后面
如123456 -> 12345612345
最后求max(dp(1,n),dp(2,n+1),......,dp(n,2n-1))
题目:环形石子合并
#include<bits/stdc++.h>
using namespace std;
const int N=210;
int n;
int dpmax[2*N][2*N],dpmin[2*N][2*N],a[2*N];
int b[2*N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i+n]=a[i];
}
for(int i=1;i<=2*n;i++)
b[i]=b[i-1]+a[i];
memset(dpmin,0x3f3f3f,sizeof(dpmin));
memset(dpmax,0,sizeof(dpmax)); //注意预处理!!!!
for(int len=1;len<=n;len++)
{
for(int i=1;i+len-1<=2*n;i++)
{
int j=i+len-1;
if(len==1)
{
dpmax[i][i]=0;
dpmin[i][i]=0;
continue;
}
for(int k=i;k+1<=j;k++)
{
dpmin[i][j]=min(dpmin[i][j],dpmin[i][k]+dpmin[k+1][j]+b[j]-b[i-1]);
dpmax[i][j]=max(dpmax[i][j],dpmax[i][k]+dpmax[k+1][j]+b[j]-b[i-1]);
}
}
}
int maxn=0,minn=0x3f3f3f;
for(int i=1;i<=n;i++)
maxn=max(dpmax[i][i+n-1],maxn),minn=min(dpmin[i][i+n-1],minn);
printf("%d\n%d",minn,maxn);
}