题目
初看此题,难免想起这道题目
但仔细想想,发现中间那个人的标记会很难搞
于是便有了如下思路
思路
dp[i][j]表示在前i个人中分成j组的最小残疾程度
便有如下状态转移方程
dp[i][j] = min(dp[i-1][j],dp[i-2][j-1]+(num[i]-num[i-1])^2)
但有以下要点
当i - 1 >= j * 3
时
dp[i-1][j]
才合法
但中间那个怎么确定前 i 个中一定存在一个没用过且比方程中的
num[i]&&num[i-1]
高呢?
就有如下技巧
读入时
for(i = 1;i <= n;i++)
Read(num[n - i + 1]);
题目说了保证升序
这样读入保证了降序
循环时
for(i = 3;i <= n;i++)
{
for(j = 1;j <= i / 3;j++)
{
//j * 3 <= i的,那么在前i个当中一定存在一个比num[i],num[i - 1]大的
//因为num[i],num[i - 1]是前i个中最小的
dp[i][j] = dp[i - 2][j - 1] + M(num[i] - num[i - 1]);
dp[i][j] = Min(dp[i][j],i - 1 >= j * 3?dp[i - 1][j]:0x7f7f7f);
}
}
code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define M(x) (x) * (x)
using namespace std;
const int MAXN = 5001;
template<typename T>
inline void Read(T &x)
{
char a = getchar();
bool f = 0;
x = 0;
while(a > '9'||a < '0') {if(a == '-') f = 1;a = getchar();}
while(a >= '0'&&a <= '9') {x = x * 10 + a - '0';a = getchar();}
if(f) x *= -1;
}
template<typename T>
inline T Min(T a,T b) {if(a < b) return a;return b;}
int num[MAXN];
int dp[MAXN][1001];
int main()
{
int n,m,i,j;
Read(m),Read(n);
for(i = 1;i <= n;i++)
Read(num[n - i + 1]);
for(i = 3;i <= n;i++)
{
for(j = 1;j <= i / 3;j++)
{
dp[i][j] = dp[i - 2][j - 1] + M(num[i] - num[i - 1]);
dp[i][j] = Min(dp[i][j],i - 1 >= j * 3?dp[i - 1][j]:0x7f7f7f);
}
}
printf("%d",dp[n][m]);
}