开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情
原文题面
题目描述
给定一个长度为 的非负数组 和一个整数 。
然后将 分解为 ,满足 且 。
定义 。
要求值最小,输出其最小值。
题目分析
由于,考虑这两个数在中的位置
可知当前状态与前面的状态如何分解无关,满足DP的无后效性。
接下来考虑如何使当前结果最优,即 最小。
可以看出其为 的一次函数,最小值应在 的最值处取到。
由于 且 , 当 时 可以取 或 , 时, 取 或 。
定义 表示 分别取到两个值时最优结果。
可以看出 由 转移而来。
最初状态将 和 初始化为0。
题目样例
input
10
5 0
2 0 1 3 4
5 1
5 3 4 3 5
7 2
7 6 5 4 3 2 1
5 1
1 2 3 4 5
5 2
1 2 3 4 5
4 0
0 1 1 1
5 5
4 3 5 6 4
4 1
0 2 1 0
3 99999
200000 200000 200000
6 8139
7976 129785 12984 78561 173685 15480
output
0
18
32
11
14
0
16
0
40000000000
2700826806
Accept代码(DP)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int f[N][2], p[N][2]; // f[i][j]表示在数组第i个位置以当前位置第j个部分为结尾的前i个位置的F
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t; cin >> t;
while (t --)
{
int n, s;
cin >> n >> s;
for (int i = 0; i < n; i ++)
{
int v; cin >> v;
if (i > 0 && i < n - 1)
{
if (v <= s) p[i][0] = 0, p[i][1] = v;
else p[i][0] = s, p[i][1] = v - s;
}
else p[i][0] = p[i][1] = v;
}
f[0][0] = f[0][1] = 0;
for (int i = 1; i < n; i ++)
{
f[i][0] = min(f[i - 1][0] + p[i - 1][1] * p[i][0], f[i - 1][1] + p[i - 1][0] * p[i][0]);
f[i][1] = min(f[i - 1][0] + p[i - 1][1] * p[i][1], f[i - 1][1] + p[i - 1][0] * p[i][1]);
}
cout << f[n - 1][0] << "\n";
}
return 0;
}