本文已参与[新人创作礼]活动,一起开启掘金创作之路
Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目描述
这是3月17日代码源div2的每日一题。
知识点:区间dp
快快变大 - 题目 - Daimayuan Online Judge
给定一个长度为 n 的数组 a1,a2,…,an,接下来进行 n−1 次操作。每次选择一个下标 xx ,将 ax 和 ax+1 合并成 (ax)×(ax+1)mod1000003 ,并且你会获得 (ax−ax+1)^2 的分数。
所以每次操作后,数组的长度将会减 1,当最后只剩下一个元素时停止操作。输出最终能获得的最大分数。
输入格式
第一行一个数字 n。
接下来一行 n 个整数 a1,a2,…,an。
输出格式
一个数,表示答案。
样例输入
3
1 2 3
样例输出
26
数据规模
所有数据保证 1≤n≤300,1≤ai≤10^6
问题解析
今天这道题其实是我投的哦哼哼哼(叉腰)。
区间dp,这题做法和石子合并 - 题目 - Daimayuan Online Judge基本一样,如果没写过石子合并题可以来2月14每日打卡这看下我以前写的题解。
我们先用一个二维数组预处理下各个区间的乘积和,s[i] [j]的意思是ai到aj的乘积。用一个二维动规数组f来做区间dp,dp[i] [j]意思是合并i到j可以得到的最大分数。然后我们从长度2开始枚举区间长度,来获得每个区间所能得到的最大分数。最后答案就是dp[1] [n],状态转移方程:
f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j] + (s[i][k]-s[k+1][j])* (s[i][k] - s[k + 1][j]));
AC代码
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
typedef long long ll;
typedef pair<ll, ll>PII;
const int N = 1010, MOD = 1000003;
ll f[N][N], s[N][N],v[N];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> v[i];
}
for (int i = 1; i <= n; i++)
{
s[i][i] = 1;
s[i][i-1] = 1;
for (int j = i; j <= n; j++)
{
s[i][j] = (v[j] * s[i][j - 1]) % 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] + (s[i][k]-s[k+1][j])* (s[i][k] - s[k + 1][j]));
}
}
}
cout << f[1][n] << endl;
return 0;
}