给定一个长度为 nn 的数组 a1,a2,…,ana1,a2,…,an,接下来进行 n−1n−1 次操作。每次选择一个下标 xx ,将 axax 和 ax+1ax+1 合并成 ax×ax+1mod1000003ax×ax+1mod1000003 ,并且你会获得 (ax−ax+1)2(ax−ax+1)2 的分数。
所以每次操作后,数组的长度将会减 11,当最后只剩下一个元素时停止操作。输出最终能获得的最大分数。
输入格式
第一行一个数字 nn。
接下来一行 nn 个整数 a1,a2,…,ana1,a2,…,an。
输出格式
一个数,表示答案。
样例输入
3
1 2 3
样例输出
26
数据规模
所有数据保证 1≤n≤300,1≤ai≤1061≤n≤300,1≤ai≤106。
思路:这个题是区间dp,这个题其实是基于分治思想,分治主要是将连续的数组进行分解,是通过枚举分界点来实现的。区间的大小是任意的,区间被分割也是任意的。第一层循环是控制数组的长度,第二层循环是控制起点但i只能到 i+len-1<=n,(第三层循环控制分界点位置)。当题目要求求出任意的连续的子串,而且操作的可能是相邻两个,也可能是多个。一般分治还会用到前缀和,这相当于优化吧。与这题十分相似的是石子合并,都是用分治来写的。分治的写法是从k分开,f[i][k] + f[k+1][j] 。
完整代码:
#include <bits/stdc++.h>
#include <cstring>
using namespace std;
#define int long long
const int mod=1000003;
const int N=400;
int a[N],sum[N][N];
int f[N][N];
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
sum[i][i]=1;
sum[i][i-1]=1;
for(int j=i;j<=n;j++)
{
sum[i][j]=(sum[i][j-1]*a[j])%mod;
}
}
for(int len=2;len<=n;len++)
{
for(int i=1;i+len-1<=n;i++)
{
int j=i+len-1;
for(int k=i;k<j;k++)
{
f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+(sum[i][k]-sum[k+1][j])*(sum[i][k]-sum[k+1][j]));
}
}
}
cout<<f[1][n]<<endl;
return 0;
}