传送门:石子合并
传送门:环形石子合并
思路:状态表示:f[i][j]表示在i~j这段区间内所能取得的最小值
状态转移方程:f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+d[j]-d[i-1]
i~j的值可以由i~k,k+1~j这两段的值加上它们合并时的产生和得来,枚举取得最小值。
时间复杂度为O(n^3)
代码1:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
#include<sstream>
using namespace std;
typedef long long LL;
const int N=310;
int n,a[N],dp[N][N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]+=a[i-1];
}
for(int len=2;len<=n;len++)
for(int i=1;i+len-1<=n;i++)
{
int l=i,r=i+len-1;
dp[l][r]=1e9;
for(int k=l;k<r;k++)
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]+a[r]-a[l-1]);
}
cout<<dp[1][n]<<endl;
return 0;
}
代码2:
思路:将n个数扩充为2*n个数,环状问题转化成链状问题。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
typedef long long LL;
const int N=420;
int f[N][N],g[N][N];
int d[N],a[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i+n]=a[i];
}
for(int i=1;i<=2*n;i++)
d[i]+=d[i-1]+a[i];
memset(f,0x3f,sizeof f);
memset(g,-0x3f,sizeof g);
for(int len=1;len<=n;len++)
for(int l=1;l+len-1<=2*n;l++)
{
int r=l+len-1;
if(len==1)
f[l][r]=g[l][r]=0;
else
for(int k=1;k<r;k++)
{
f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+d[r]-d[l-1]);
g[l][r]=max(g[l][r],g[l][k]+g[k+1][r]+d[r]-d[l-1]);
}
}
int sum=1e9;
int sum1=-1e9;
for(int i=1;i<=n;i++)
{
sum=min(sum,f[i][i+n-1]);
sum1=max(sum1,g[i][i+n-1]);
}
cout<<sum<<endl;
cout<<sum1<<endl;
return 0;
}