您的位置:首页 > 其它

斜率优化dp专题学习

2016-05-12 20:53 288 查看
推荐大牛博客:http://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html

我就不一一赘述了,反而会推的莫名其妙。

就说一点把,斜率优化dp基本上都是可以化到 dp[i]=min(dp[j]+cost(j+1,i)) 这样的形式,二维的就是dp[i][m]=min(dp[j][m-1]+cost(j+1,i));

然后每次假设k<j<i,然后假设j比k更优,就能得到一个式子,化简得到 (yj-yk)/(xj-xk) < f[i],这样的形式,令g(j,k)=(yj-yk)/(xj-xk) , g(j,k) < f[i],表示j比k更优

所以每次通过化简得到的式子之后,我们可以用单调队列来O(n)(二维是O(nm))的维护最优解,第一个循环应该是在单调队列的头上找最优解点,如果g(head+1,head)<f[i],表示head+1比head更优,所以head++,直到不成立,然后求出dp[i].

之后是维护单调队列,把i放进去,i对以后答案产生的影响,就根据g(i,tail-1) 和g(tail-1,tail-2)来比较,如果g(i,tail-1) < g(tail-1, tail-2) ,对于以后一个解dp[x],如果g(tail-1,tail-2) < dp[x],则g(i,tail-1) < dp[x],表明tail-1比tail-2更优,但是i比tail-1更优,所以tail--,直到不成立,把i放进去即可

主要实现的过程就是这样,二维的话也同理,在求g(j,k)的时候需要用到上一轮求出来的结果呗。

如果是套路题,那么主要的难点就在于如何O(1)的求解cost了

题目解析:

hdu 3507

http://acm.hdu.edu.cn/showproblem.php?pid=3507

斜率优化基础题:cost(j+1,i)=(sum[i]-sum[j])^2+M,其余完全符合上述条件,直接套路过

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX           500005
#define   MAXN          1000005
#define   maxnode       10
#define   sigma_size    2
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
const double inf   = 1e18;
const double eps   = 1e-9;
const LL     mod   = 1e9+7;
const ull    mxx   = 1333331;

/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/

LL sum[MAX];
int q[MAX];
int n,m;
LL dp[MAX];
LL getup(int i,int j){
return dp[i]+sum[i]*sum[i]-dp[j]-sum[j]*sum[j];
}

LL getdown(int i,int j){
return 2*(sum[i]-sum[j]);
}

LL getdp(int i,int j){
return dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]);
}
int main(){
//freopen("in.txt","r",stdin);
while(cin>>n>>m){
sum[0]=0;
for(int i=1;i<=n;i++){
int a;
scanf("%d",&a);
sum[i]=sum[i-1]+a;
}
int head=0,tail=0;
q[tail++]=0;
mem(dp,0);
for(int i=1;i<=n;i++){
while(tail>head+1&&getup(q[head+1],q[head])<=sum[i]*getdown(q[head+1],q[head])) head++;
dp[i]=getdp(i,q[head]);
while(tail>head+1&&getup(i,q[tail-1])*getdown(q[tail-1],q[tail-2])<=getup(q[tail-1],q[tail-2])*getdown(i,q[tail-1])) tail--;
q[tail++]=i;
}
cout<<dp
<<endl;
}
return 0;
}


hdu 2829

http://acm.hdu.edu.cn/showproblem.php?pid=2829

也是给你一段n个数字,然后让你切m刀,分成m+1段,每段的cost(j+1,i)=((sum[i]-sum[j])^2-(sum[i]^2-sum[j]^2))/2;

通过记录两个数组,即可O(1)的求cost,然后二维的斜率优化,和一维其实是一样的,只要在求斜率的时候多传进去一个这是第j次,然后用j-1次的数据即可

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define MAX 1005
#define MAXN 1000005
#define maxnode 10
#define sigma_size 2
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define middle int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
#define pii pair<int,int>
#define bits(a) __builtin_popcount(a)
#define mk make_pair
#define limit 10000

//const int prime = 999983;
const int INF = 0x3f3f3f3f;
const LL INFF = 0x3f3f;
const double pi = acos(-1.0);
const double inf = 1e18;
const double eps = 1e-9;
const LL mod = 1e9+7;
const ull mxx = 1333331;

/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/

LL sum[MAX];
LL sum2[MAX];
LL dp[MAX][MAX];
int q[MAX];

LL getup(int j,int k,int x){
return dp[j][x-1]+(sum[j]*sum[j]+sum2[j])/2-dp[k][x-1]-(sum[k]*sum[k]+sum2[k])/2;
}

LL getdown(int j,int k,int x){
return sum[j]-sum[k];
}

LL getdp(int i,int j,int x){
return dp[j][x-1]+((sum[i]-sum[j])*(sum[i]-sum[j])-(sum2[i]-sum2[j]))/2;
}

int main(){
//freopen("in.txt","r",stdin);
int n,m;
while(cin>>n>>m){
if(n==0&&m==0) break;
sum[0]=sum2[0]=0;
for(int i=1;i<=n;i++){
LL a;
scanf("%I64d",&a);
sum[i]=sum[i-1]+a;
sum2[i]=sum2[i-1]+a*a;
}
mem(dp,INF);
dp[0][0]=0;
for(int j=1;j<=m+1;j++){
int head=0,tail=0;
q[tail++]=0;
for(int i=1;i<=n;i++){
while(tail>head+1&&getup(q[head+1],q[head],j)<=sum[i]*getdown(q[head+1],q[head],j)) head++;
dp[i][j]=getdp(i,q[head],j);
while(tail>head+1&&getup(i,q[tail-1],j)*getdown(q[tail-1],q[tail-2],j)<=getup(q[tail-1],q[tail-2],j)*getdown(i,q[tail-1],j)) tail--;
q[tail++]=i;
}
}
cout<<dp
[m+1]<<endl;
}
return 0;
}

poj 2018

http://poj.org/problem?id=2018

给你一段长度n的数,然后找出连续长度大于等于m的段里面最大的平均值

这题乍一看,很难和斜率优化的模型套上关系啊,但是仔细看呢,其实一段的平均值,(sum[i]-sum[j])/(i-j),这不就是一段斜率么

然后因为长度要大于等于m,所以i直接从m开始计数,把i-m和单调队列中的点进行维护

如何维护呢,首先i-m先要放进去,因为这个点最后也有可能作为最优解的,放的过程和前面一样,直接找斜率,如果g(i-m,tail-1)<g(tail-1,tail-2),那么tail--

然后放进去之后呢,这是一个下凸壳的形状,假设现在考察i点,下凸壳上的点都有可能作为最优解,为啥刚才舍去的点不可能呢,可以画图来看,如果是上凸壳,对于后面的点,i必然比tail-1更优,所以可以舍去上凸壳,只保留下凸壳,然后下凸壳上如果求解呢,通过画图可知,下凸壳上的点,有个临界值,临界值之前,后面的点比前面的点更优,临界值之后,前面的点比后面的点更优,所以需要在凸壳上二分,寻找最优的临界点j,这个临界点就是dp[i]的最优解,dp[i]=(sum[i]-sum[j])/(i-j);

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define MAX 100005
#define MAXN 1000005
#define maxnode 10
#define sigma_size 2
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define middle int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
#define pii pair<int,int>
#define bits(a) __builtin_popcount(a)
#define mk make_pair
#define limit 10000

//const int prime = 999983;
const int INF = 0x3f3f3f3f;
const LL INFF = 0x3f3f;
const double pi = acos(-1.0);
const double inf = 1e18;
const double eps = 1e-9;
const LL mod = 1e9+7;
const ull mxx = 1333331;

/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/

double sum[MAX];
int q[MAX];

double getup(int j,int k){
return sum[j]-sum[k];
}

double getdown(int j,int k){
return j-k;
}

int main(){
//freopen("in.txt","r",stdin);
int n,m;
cin>>n>>m;
sum[0]=0;
for(int i=1;i<=n;i++){
int a;
RI(a);
sum[i]=sum[i-1]+a;
}
int top=0;
//q[top++]=0;
double ans=0;
for(int i=m;i<=n;i++){
int j=i-m;
while(top>1&&getup(j,q[top-1])*getdown(q[top-1],q[top-2])<=getup(q[top-1],q[top-2])*getdown(j,q[top-1])) top--;
q[top++]=j;
int l=0,r=top-2;
while(l<=r){
int mid=(l+r)/2;
if(getup(i,q[mid])*getdown(i,q[mid+1])<=getup(i,q[mid+1])*getdown(i,q[mid])) l=mid+1;
else r=mid-1;
}
ans=max(ans,getup(i,q[l])/getdown(i,q[l]));
}
int ret=ans*1000;
printf("%d\n",ret);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: