斜率优化dp
使用斜率优化是有条件的,在k(i)*g(j)把k(i)作斜率,g(j)横轴,必须保证都是单调递增的,如果不是就要通过添加负号改变单调性
这里不理解就看蓝书的任务安排
直线斜率:最优解,逼近(i)
点斜率:决策点,找最优(j)
截距:代表最优解的值,尽量(fi)
单调性要求:斜率c(i),横坐标s(j)
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<stack> #include<cstring> #include<map> #include<cmath> #include<vector> /* f[i][j]前i个数取出j个的最大值 f[i][j]=max(f[j][k]+(sum[i]-sum[j])*(sum[j]-sum[k])),0<=k<j f[j][k]=(s[i]-s[j])*s[k]+f[i][j]-s[i]*s[j]+s[j]^2 k=s[i]-s[j],截距只和i,j有关(固定就好了 但是发现k不具有单调性,s[k] 也没有单调性,怎么办? 神奇的性质!!!! 直接给每个点排序 保证s[k]递增 让后就变成了一般的DP j为固定的状态,因为这样DP满足前面的我要用的已经DP过了 第一步寻找决策点 k: 最大值,维护上凹包 第二部用k更新状态i: 倒序满足可以删除队尾 */ using namespace std; const long long N=5010; long long H,L,R; long long F[N][N],plu[N],D[N]; long long ans; struct poi{long long sig,val;}num[N]; inline bool operator <(const poi a,const poi b) {return a.val<b.val;} inline long long read() { long long x=0,f=1; char c; while(!isdigit(c=getchar())) if(c=='-') f=-1; do x=(x<<1)+(x<<3)+(c^48); while(isdigit(c=getchar())); return x*f; } double sloup(long long j,long long a,long long b){ return 1.0*(double)(F[j][a]-F[j][b])/(plu[a]-plu[b]); } int main() { freopen("d.in","r",stdin); freopen("d.out","w",stdout); H=read(); memset(F,-0x3f,sizeof(F)); for(long long i=1;i<=H;i++) { long long x=read(); plu[i]=plu[i-1]+x; num[i]=(poi){i,plu[i]}; F[i][0]=0; } F[0][0]=0; sort(num,num+H+1); for(long long j=1;j<=H;j++)//f[i][j]=f[j][k]+(s[i]-s[j])*(s[j]-s[k]) { L=1,R=0; for(long long k=0;k<=H;k++) { if(num[k].sig>=j) continue; while(L<R&&sloup(j,D[R],D[R-1])<=sloup(j,num[k].sig,D[R-1])) R--; D[++R]=num[k].sig;//删除时必须取到等号,斜率相同,越往后截距越大 //s[k]单调递增,f[j][k]不一定,正是我要维护的 } for(long long i=H;~i;i--) //为什么倒序?因为我要保证斜率单调递减才能删除队尾,但是正序是递增,所以倒序 //s[i]-s[j],s[j]固定,s[i]递变 { if(num[i].sig<=j) continue; while(L<R&&(plu[num[i].sig]-plu[j])<=sloup(j,D[L+1],D[L])) L++;//这里斜率相等,等效点,删前面的 F[num[i].sig][j]=max(F[num[i].sig][j],F[j][D[L]]+(plu[num[i].sig]-plu[j])*(plu[j]-plu[D[L]])); } } ans=-0x3fffffff; for(long long i=0;i<H;i++) ans=max(ans,(long long)F[H][i]); printf("%lld",ans); return 0; }
J. 玩具装箱
内存限制:512 MiB时间限制:1000 ms标准输入输出
题目类型:传统评测方式:文本比较
题目描述
原题来自:HNOI 2008
P 教授要去看奥运,但是他舍不得他的玩具,于是他决定把所有的玩具运到北京。
他使用自己的压缩器进行压缩。这个压缩器可以将任意物品变成一维,再放到一种特殊的一维容器中。P 教授有编号为 的 件玩具,玩具经过压缩后会变成一维,第 件件玩具压缩后长度为 。
为了方便整理,P 教授要求:
- 在一个一维容器中,玩具的编号是连续的;
- 如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物。形式地说,如果要将 号玩具到 号玩具 放到同一个容器中,则容器长度不小于 。
制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 ,其制作费用为 ,其中 是一个常量。
P 教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过 。试求最小费用。
输入格式
第一行输入两个整数 ;
接下来 行,每行一个整数 。
输出格式
输出最小费用。
这种和区间划分有关的dp很容易列出dp方程 dp[i]=min(dp[j]+(sum[i]-sum[j]+i-j-1-L)^2)
鐜式子化简,把和ij都有关的部分提出来,i部分作为斜率,横坐标是j部分,纵坐标就是只和j有关的部分,截距就是i点的最优决策
最大值,维护上凸包,在i决策前维护k最逼近的值,决策后把i放入维护斜率单调递减
const int N=100000+10; int n,l; ll sum[N],dp[N]; int head,tail,q[N]; inline double slope(int i,int j) { return ((dp[i]+(sum[i]+i)*(sum[i]+i))-dp[j]-(sum[j]+j)*(sum[j]+j))/(sum[i]+i-sum[j]-j); } int main() { n=re(),l=re(); _f(i,1,n) { scanf("%lldf",&sum[i]); sum[i]+=sum[i-1]; } head=tail=1; _f(i,1,n) { while(head<tail&&slope(q[head],q[head+1])<2*(sum[i]+i-l-1))head++; dp[i]=dp[q[head]]+(sum[i]+i-sum[q[head]]-q[head]-1-l)*(sum[i]+i-sum[q[head]]-q[head]-1-l); while(head<tail&&slope(i,q[tail])<slope(q[tail-1],q[tail]))--tail; q[++tail]=i; } chu("%lld",dp[n]); return 0; }
浙公网安备 33010602011771号