20200901 练习:斜率优化

总览:

化为 k x + b = y kx+b=y kx+b=y,并且要求的仅在 b b b
将每个 x , y x,y x,y 看成一个点,用斜率为 k k k 的线去切,最大最小化截距
最优的点在凸包上,维护凸包即可
在这里插入图片描述

T1 P2900 [USACO08MAR]Land Acquisition G

思路:
先按长 a a a 排序
发现对于两个矩形 i , j i,j i,j
如果 a i < a j , b i > b j a_i<a_j,b_i>b_j ai<aj,bi>bj,则可以不考虑 j j j
于是可以去掉一些矩形
此时长 a a a 单增,宽 b b b 单减
f i f_i fi 为前 i i i 个矩形的最小花费
d p dp dp 方程:
f i = m i n j = 1 i ( f j + a i b j ) f_i=min_{j=1}^{i}(f_j+a_ib_j) fi=minj=1i(fj+aibj)
对于每一次转移,方程为 − b j a i + f i = f j -b_ja_i+f_i=f_j bjai+fi=fj a i a_i ai 为定值,可视为斜率,则点为 ( − b j , f j ) (-b_j,f_j) (bj,fj)
维护凸包即可

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace IO{
char _buf_[1<<21],*_p1_=_buf_,*_p2_=_buf_;
#define ch() (_p1_==_p2_&&(_p2_=(_p1_=_buf_)+fread(_buf_,1,1<<21,stdin),_p1_==_p2_)?EOF:*_p1_++)
inline int in(){
	int s=0,f=1;char x=ch();
	while(x<'0'||x>'9'){
		if(x=='-')	f=-1;
		x=ch();
	}
	while(x>='0'&&x<='9'){
		s=(s*10)+(x&15);
		x=ch();
	}
	return f==1?s:-s;
}
char _buf[1<<21];
int _p1=-1;
inline void flush(){
	fwrite(_buf,1,_p1+1,stdout);
	_p1=-1;
}
inline void pc(char x){
	if(_p1==(1<<21)-1)	flush();
	_buf[++_p1]=x;
}
inline void out(int x){
	char k[30];
	int pos=0;
	if(!x){
		pc('0');
		return;
	}
	if(x<0){
		pc('-');
		x=-x;
	}
	while(x){
		k[++pos]=(x%10)|48;
		x/=10;
	}
	for(int i=pos;i;i--)	pc(k[i]);
	return;
}
}
using namespace IO;

const int A=1e5+5;
int n;
struct node{
	int a,b;
}p[A];
int tot;

inline bool cmp(node u,node v){
	if(u.a!=v.a)	return u.a>v.a;
	return u.b>v.b;
}

int q[A],l=1,r=0;
int f[A];

inline int calc(int x,int y){
	return (f[x]-f[y])/(p[y+1].a-p[x+1].a);
}

signed main(){
	n=in();
	for(int i=1;i<=n;i++)
		p[i].a=in(),p[i].b=in();
	sort(p+1,p+1+n,cmp);
	for(int i=1;i<=n;i++)
		if(p[i].b>p[tot].b)	p[++tot]=p[i];
	q[++r]=0;
	for(int i=1;i<=tot;i++){
		while(l<r&&calc(q[l],q[l+1])<=p[i].b)	l++;
		f[i]=f[q[l]]+p[q[l]+1].a*p[i].b;
		while(l<r&&calc(q[r-1],q[r])>=calc(q[r],i))	r--;
		q[++r]=i;
	}
	out(f[tot]),pc('\n');
	flush();
	return 0;
}

T2 P3195 [HNOI2008]玩具装箱

思路:
求前缀和 s i s_i si
f i f_i fi 为前 i i i 个满足时的最优解
d p dp dp 方程:
f i = m i n j = 0 i ( f j + ( ( s i − s j + i − j − 1 ) − L ) 2 ) ( 第 j + 1 到 i 分 成 一 个 ) f_i=min_{j=0}^{i}(f_j+((s_i-s_j+i-j-1)-L)^2)(第j+1到i分成一个) fi=minj=0i(fj+((sisj+ij1)L)2)j+1i
a i = s i + i , b i = s j + j + L + 1 a_i=s_i+i,b_i=s_j+j+L+1 ai=si+i,bi=sj+j+L+1

f i = m i n j = 0 i ( f j + ( a i − b j ) 2 ) f i = f j + a i 2 + b j 2 − 2 × a i b j 2 a i × b j + ( f i − a i 2 ) = f j + b j 2 f_i=min_{j=0}^i(f_j+(a_i-b_j)^2)\\ f_i=f_j+a_i^2+b_j^2-2\times a_ib_j\\ 2a_i\times b_j+(f_i-a_i^2)=f_j+b_j^2 fi=minj=0i(fj+(aibj)2)fi=fj+ai2+bj22×aibj2ai×bj+(fiai2)=fj+bj2
斜率为 2 × a i 2\times a_i 2×ai,点为 ( b j , f j + b j 2 ) (b_j,f_j+b_j^2) (bj,fj+bj2),最小化截距

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace IO{
char _buf_[1<<21],*_p1_=_buf_,*_p2_=_buf_;
#define ch() (_p1_==_p2_&&(_p2_=(_p1_=_buf_)+fread(_buf_,1,1<<21,stdin),_p1_==_p2_)?EOF:*_p1_++)
inline int in(){
	int s=0,f=1;char x=ch();
	while(x<'0'||x>'9'){
		if(x=='-')	f=-1;
		x=ch();
	}
	while(x>='0'&&x<='9'){
		s=(s*10)+(x&15);
		x=ch();
	}
	return f==1?s:-s;
}
char _buf[1<<21];
int _p1=-1;
inline void flush(){
	fwrite(_buf,1,_p1+1,stdout);
	_p1=-1;
}
inline void pc(char x){
	if(_p1==(1<<21)-1)	flush();
	_buf[++_p1]=x;
}
inline void out(int x){
	char k[30];
	int pos=0;
	if(!x){
		pc('0');
		return;
	}
	if(x<0){
		pc('-');
		x=-x;
	}
	while(x){
		k[++pos]=(x%10)|48;
		x/=10;
	}
	for(int i=pos;i;i--)	pc(k[i]);
	return;
}
}
using namespace IO;

const int A=1e5+5;
int n,L;
int a[A],s[A];
int f[A];
int q[A],l=1,r=0;

inline int S(int u){
	return s[u]+u;
}
inline int T(int u){
	return s[u]+u+L+1;
}
inline int X(int u){
	return T(u);
}
inline int Y(int u){
	return f[u]+T(u)*T(u);
}
inline int calc(int u,int v){
	return (Y(v)-Y(u))/(X(v)-X(u));
}

signed main(){
	n=in(),L=in();
	for(int i=1;i<=n;i++){
		a[i]=in();
		s[i]=s[i-1]+a[i];
	}	
	q[++r]=0;
	for(int i=1;i<=n;i++){
		while(l<r&&calc(q[l],q[l+1])<2*S(i))	l++;
		f[i]=f[q[l]]+(S(i)-T(q[l]))*(S(i)-T(q[l]));
		while(l<r&&calc(i,q[r-1])<calc(q[r-1],q[r]))	r--;
		q[++r]=i;
	}
	out(f[n]),pc('\n');
	flush();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值