发现我的斜率DP模板挺不错的啊!
裸题。
只不过变为了2维。 核心参考价值不大。
学习斜率DP请参考我另一篇博客:http://blog.csdn.net/baidu_23081367/article/details/52077835
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 1000 + 100;
int n, m;
LL a[maxn], s[maxn];
LL q[maxn], tail, head;
LL f[maxn][maxn];
LL c[maxn];
const LL inf = 1LL<<50;
/*
f[i][j] = f[k][j-1] + s[i]-s[k]
*/
int p;
LL y(int k)
{
return f[k][p-1] - c[k] + s[k] * s[k];
}
LL x(int k)
{
return s[k];
}
bool slope(int i, int j, int k) //得出j,k斜率
{
//判断i,j的斜率,和j,k的斜率谁更大
//如果i,j斜率<=j,k斜率,那么就需要退了
return (y(i) - y(j)) * (x(j) - x(k)) <= (y(j) - y(k)) * (x(i) - x(j));
}
LL zhuan(int j, int i)//j<i,j向i转移的值
{
//return f[j] + (s[i] - s[j]) * (s[i] - s[j]) + m;
return f[ j ][ p - 1 ] + c[ i ] - c[ j ] - s[ j ]*(s[ i ]-s[ j ]);
}
void init()
{
tail = head = 0; //tail == head则为空
for (int i = 1; i <= n; ++ i) scanf("%lld", &a[i]); //这里特别注意:用1下标
memset(s, 0, sizeof(s));
memset(c, 0, sizeof(c));
for (int i = 1; i <= n; ++ i) s[i] = s[i - 1] + a[i];
for (int i = 1; i <= n; ++ i) c[i] = c[i - 1] + s[i-1]*a[i];
q[tail++] = 0;
}
void dp() //求f[...][p]
{
head = tail = 0;
q[tail++]=0;
for (int i = 1; i <= n; ++ i)
{
while (head + 1 != tail && zhuan(q[head], i) >= zhuan(q[head + 1], i)) //如果队列里元素超过1个,那么就比较
++head;
f[i][p] = zhuan(q[head], i);
while (head + 1 != tail && slope(i, q[tail - 1], q[tail - 2]))
--tail;
q[tail++] = i;
}
}
void doit()
{
for (int i = 1; i <= n; ++ i)
{
f[i][1] = c[i];
}
for (p=2; p <= m+1; ++ p)
{
dp();
}
cout << f[n][m+1] << endl;
}
int main()
{
while (~scanf("%d%d", &n, &m))
{
if (!n && !m) break;
init();
doit();
}
return 0;
}