您的位置:首页 > 其它

[kuangbin带你飞]专题二十 斜率DP

2016-04-06 19:16 411 查看
ID
Origin
Title
20 / 60Problem AHDU 3507Print Article
13 / 19Problem BHDU 2829Lawrence
1 / 5Problem CHDU 4528小明系列故事――捉迷藏
5 / 6Problem DHDU 1300Pearls
0 / 42Problem EHDU 2993MAX Average Problem
1 / 20Problem FUVALive 5097Cross the Wall
5 / 12Problem GHDU 3045Picnic Cows
2 / 4Problem HHDU 3516Tree Construction
3 / 4Problem IPOJ 1160Post Office
3 / 5Problem JPOJ 1180Batch Scheduling
3 / 3Problem KPOJ 2018Best Cow Fences
2 / 4Problem LPOJ 3709K-Anonymous Sequence
Problem MPOJ 2841Navigation Game
2 / 2Problem NPOJ 1260Pearls
2 / 4Problem OUVA 12594Naming Babies
3 / 4Problem PHDU 3480Division
1 / 1Problem QUVALive 6771Buffed Buffet
20 / 60 Problem A HDU 3507 Print Article

此题是很基础的斜率DP的入门题。
题意很清楚,就是输出序列a
,每连续输出的费用是连续输出的数字和的平方加上常数M
让我们求这个费用的最小值。
设dp[i]表示输出前i个的最小费用,那么有如下的DP方程:
dp[i]= min{ dp[j]+(sum[i]-sum[j])^2 +M } 0<j<i
其中 sum[i]表示数字的前i项和。
相信都能理解上面的方程。
直接求解上面的方程的话复杂度是O(n^2)
对于500000的规模显然是超时的。下面讲解下如何用斜率优化DP使得复杂度降低一维。

我们首先假设在算 dp[i]时,k<j ,j点比k点优。
也就是
dp[j]+(sum[i]-sum[j])^2+M <= dp[k]+(sum[i]-sum[k])^2+M;
所谓j比k优就是DP方程里面的值更小
对上述方程进行整理很容易得到:
[(dp[j]+sum[j]*sum[j])-(dp[k]+sum[k]*sum[k])] / 2(sum[j]-sum[k]) <=sum[i].
注意整理中要考虑下正负,涉及到不等号的方向。
左边我们发现如果令:yj=dp[j]+sum[j]*sum[j] xj=2*sum[j]
那么就变成了斜率表达式:(yj-yk)/(xj-xk) <= sum[i];
而且不等式右边是递增的。
所以我们可以看出以下两点:我们令g[k,j]=(yj-yk)/(xj-xk)
第一:如果上面的不等式成立,那就说j比k优,而且随着i的增大上述不等式一定是成立的,也就是对i以后算DP值时,j都比k优。
那么k就是可以淘汰的。
第二:如果 k<j<i 而且 g[k,j]>g[j,i] 那么 j 是可以淘汰的。
假设 g[j,i]<sum[i]就是i比j优,那么j没有存在的价值
相反如果 g[j,i]>sum[i] 那么同样有 g[k,j]>sum[i] 那么 k比 j优 那么 j 是可以淘汰的

所以这样相当于在维护一个下凸的图形,斜率在逐渐增大。
通过一个队列来维护。

/*
* 用dp[i][x]表示前i个点,炸掉x条边可以破坏的最大值
* 答案就是tol-dp
[m]
* dp[i][x]=max{dp[j][x-1]+sum[j]*(sum[i]-sum[j])}  x-1<j<i
* 假设在计算i时,k<j,j比k点优
* dp[k][x-1]+sum[k]*(sum[i]-sum[k])<=dp[j][x-1]+sum[j]*(sum[i]-sum[j])
* 化简得 ( (sum[j]*sum[j]-dp[j][x-1])-(sum[k]*sum[k]-dp[k][x-1]) ) /(sum[j]-sum[k]  <=sum[i]
*
* yj=sum[j]*sum[j]-dp[j][x-1]    xj=sum[j]
* (yj-yk)/(xj-xk)<=sum[i]
* 右边不等式是递增的
* g[k,j]=(yj-yk)/(xj-xk)
* 上述不等式成立说明j比k优
* 如果k<j<i  g[k,j]>g[i,j]那么k可以淘汰
* 如果g[j,i]<sum[i]  j可以淘汰
*
*
*/

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int MAXN=1010;
int n,m;
int a[MAXN];
int sum[MAXN];
int dp[MAXN][MAXN];
int tol;
int getDP(int i,int x,int j)
{
return dp[j][x-1]+sum[j]*(sum[i]-sum[j]);
}
int getUp(int j,int x,int k)
{
return sum[j]*sum[j]-dp[j][x-1]-(sum[k]*sum[k]-dp[k][x-1]);
}
int getDown(int j,int k)
{
return sum[j]-sum[k];
}
int q[MAXN];
void solve()
{
memset(dp,0,sizeof(dp));
int front,rear;
for(int x=1;x<=m;x++)
{
rear=front=0;
q[rear++]=x;
for(int i=x+1;i<=n;i++)
{
while(front+1<rear && getUp(q[front+1],x,q[front])<=sum[i]*getDown(q[front+1],q[front]))
front++;
dp[i][x]=getDP(i,x,q[front]);
while(front+1<rear && getUp(i,x,q[rear-1])*getDown(q[rear-1],q[rear-2])<=getUp(q[rear-1],x,q[rear-2])*getDown(i,q[rear-1]))
rear--;
q[rear++]=i;
}
}
printf("%d\n",tol-dp
[m]);
}
int main()
{
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
while(scanf("%d%d",&n,&m)==2)
{
if(n==0 && m==0)break;
sum[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
tol=0;
for(int i=n;i>1;i--)
tol+=a[i]*sum[i-1];
solve();
}
return 0;
}


View Code

1 / 5 Problem C HDU 4528 小明系列故事――捉迷藏
5 / 6 Problem D HDU 1300 Pearls
0 / 42 Problem E HDU 2993 MAX Average Problem
1 / 20 Problem F UVALive 5097 Cross the Wall
5 / 12 Problem G HDU 3045 Picnic Cows
2 / 4 Problem H HDU 3516 Tree Construction
3 / 4 Problem I POJ 1160 Post Office
3 / 5 Problem J POJ 1180 Batch Scheduling
3 / 3 Problem K POJ 2018 Best Cow Fences
2 / 4 Problem L POJ 3709 K-Anonymous Sequence
Problem M POJ 2841 Navigation Game
2 / 2 Problem N POJ 1260 Pearls
2 / 4 Problem O UVA 12594 Naming Babies
3 / 4 Problem P HDU 3480 Division
1 / 1 Problem Q UVALive 6771 Buffed Buffet
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: