# 斜率优化dp学习 + 模板

Picnic Cows（HDU3045）

f[i] = min( f[j] + (a[j + 1 -> i] - Min k) ) （0 <= j < i）

– –> f[i] = min( f[j] + sum[i] - sum[j] - a[j + 1] * ( i - j ) )

Min k 代表序列 j + 1 -> i 内的最小值，排序后可以简化为a[j + 1]。提取相似项合并成前缀和sum。这个方程的思路就是枚举 j 不断地计算状态值更新答案。但是数据规模达到了 40000 ，这种以O（n ^ 2）为绝对上界方法明显行不通。所以接下来我们要引入“斜率”来优化。

f[i] = f[j] + sum[i] - sum[j] - a[j + 1] * ( i - j )
– –> f[i] = (f[j] - sum[j] + a[j + 1] * j) - i * a[j + 1] + sum[i]
（此步将只由i决定的量与只由j决定的量分开）

a[j + 1] = x
f[j] - sum[j] + a[j + 1] * j = y
f[i] = b

– –> b = y - k * x

– –> y = k * x + b

code：

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

typedef long long dnt;

int n, T, Q[405005];
dnt sum[405005], f[405005], a[405005];

dnt Y( int i, int j )
{
return f[j] - sum[j] + j * a[j + 1] - (f[i] - sum[i] + i * a[i + 1]);
}

dnt X( int i, int j )
{
return a[j + 1] - a[i + 1];
}

dnt DP( int i, int j )
{
return f[j] + (sum[i] - sum[j]) - (i - j) * a[j + 1];
}

inline dnt R()
{
static char ch;
register dnt res, T = 1;
while( ( ch = getchar() ) < '0'  || ch > '9' )if( ch == '-' )T = -1;
res = ch - 48;
while( ( ch = getchar() ) <= '9' && ch >= '0')
res = res * 10 + ch - 48;
return res*T;
}

int main()
{
sum[0] = 0;
while(~scanf( "%d%d", &n, &T ))
{
a[0] = 0, f[0] = 0;
for(int i = 1; i <= n; i++)
scanf( "%I64d", &a[i] );
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + a[i];
int h = 0, t = 0;
Q[++t] = 0;
for(int i = 1; i <= n; i++)
{
int cur = i - T + 1;
for(; h + 1 < t && Y(Q[h + 1], Q[h + 2]) <= i * X(Q[h + 1], Q[h + 2]); h++);
f[i] = DP(i, Q[h + 1]);
if(cur < T) continue;
for(; h + 1 < t && Y(Q[t - 1], Q[t]) * X(Q[t], cur) >= X(Q[t - 1], Q[t]) * Y(Q[t], cur); t--);
Q[++t] = cur;
}
printf( "%I64d\n", f[n] );
}
return 0;
}

HDU 5956 The Elder（斜率优化DP）


#include<bits/stdc++.h>
using namespace std;
int n,p;
typedef long long dnt;
int  Q[405005];
dnt sum[405005], f[405005], a[405005];
const int N = 2e5+7;
int stk[N],L,R;
/*

*/
dnt ans;
struct node
{
int u,v,w;
int next;
} edge[N];
{
edge[cnt].v =  v;
edge[cnt].w = w;

edge[cnt].v = u;

}

dnt Y( int j, int i )
{

return f[j]-f[i]+(sum[j]*sum[j])-sum[i]*sum[i];
//   return f[j] - sum[j] + j * a[j + 1] - (f[i] - sum[i] + i * a[i + 1]);
}

dnt X( int j, int i )
{

// return a[j + 1] - a[i + 1];
return sum[j] - sum[i];

//树上前缀和不用-1
}

dnt DP( int i, int j )
{
// return f[j] + (sum[i] - sum[j]) - (i - j) * a[j + 1];
return f[j]+p+(sum[i]-sum[j])*(sum[i]-sum[j]);
}
//dnt Xie(int k,int i)
//{
//    return (Y(k)-Y(i))/(X(k)-X(i));
//}
void dfs(int now,int fa)
{
vector<pair<int,int> >sv;
int l =L,r =R;//l,r 保存现场存储L,R
//假设存在j<k<i
//且f[k]<f[j] 即k比j优则淘汰掉j

//队尾元素L
while(L+1<R&&Y(stk[L+1],stk[L])<=2LL*sum[now]*X(stk[L+1],stk[L]))
{
sv.push_back(make_pair(L,stk[L]));
L++;
}
if(now!=1)
{
// f[now] = DP(now,stk[L]);
f[now]=f[stk[L]]+(1LL*sum[now]-sum[stk[L]])*(1LL*sum[now]-sum[stk[L]])+p;

ans = max(ans,f[now]);
}
//队尾元素R-1
while(L+1<R&&Y(stk[R-1],stk[R-2])*X(now,stk[R-1])>=Y(now,stk[R-1])*X(stk[R-1],stk[R-2]))
{
R--;
sv.push_back(make_pair(R,stk[R]));
}
stk[R++] = now;
{
int v = edge[i].v;
if(v==fa)
{
continue;
}
sum[v]  =sum[now]+edge[i].w;
dfs(v,now);

}

L = l;
R = r;
//恢复 现场
int SZ = sv.size();
for(int i=0; i<SZ; i++)
{

int pos= sv[i].first;
int id = sv[i].second;
stk[pos] = id;
}

}
int main()
{

int T;
cin>>T;
while(T--)
{
cnt =0;

memset(sum,0,sizeof sum);
cin>>n>>p;
int u,v,w;
for(int i=1; i<=n-1; i++)
{

scanf("%d%d%d",&u,&v,&w);
}

ans = 0;
L = R =0;
f[1] = -p;
dfs(1,0);
cout<<ans<<endl;

}
return 0;
}
in:
1
6 50
1 2 4
2 3 5
1 4 3
4 5 3
5 6 3
out:81

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客