玩具装箱&土地购买
分类:
IT文章
•
2023-03-24 19:31:37
今天一天8h 写了两道斜率优化的题(别问我效率为什么这么低 代码bug太多了)
关键是思考的不周全 估计是写的题少手生 以后就会熟练起来了吧。
这道题显然有一个n^2的dp方程
然后 双端队列维护一下注意一下细节即可得出答案 亲测不会爆long long
关键是代码别打错什么+1 -1少了的问题也可能只有我这个NC 才会打出来。
//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
using namespace std;
char buf[1<<15],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline long long read()
{
long long x=0,f=1;char ch=getc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
return x*f;
}
inline void put(long long x)
{
x<0?x=-x,putchar('-'):0;
long long num=0;char ch[50];
while(x)ch[++num]=x%10+'0',x/=10;
num==0?putchar('0'):0;
while(num)putchar(ch[num--]);
putchar(10);return;
}
//针对这道HNOI 2008
//考虑dp
//设f[i]表示前i件物品放好的最小费用
//f[i]=min(f[i],f[j]+((i-j-1)*(Ci-Cj)-L)^2);
const long long MAXN=50002;
long long n,L;
long long f[MAXN],C[MAXN];
long long q[MAXN],l,r;
long long y(long long x)
{
return q[x]+C[q[x]];
}
int main()
{
//freopen("1.in","r",stdin);
n=read();L=read();
for(long long i=1;i<=n;i++)
{
C[i]=read();
C[i]+=C[i-1];
}
for(long long i=1;i<=n;i++)f[i]=INF;
f[0]=0;q[++r]=0;l=r;
for(long long i=1;i<=n;i++)
{
long long k=i-1+C[i]-L;
while(l<r&&(f[q[l+1]]-f[q[l]]+y(l+1)*y(l+1)-y(l)*y(l)<=2*k*(y(l+1)-y(l))))l++;
long long x=y(l);
//f[i]=min(f[i],f[j]+((i-j-1)+(C[i]-C[j])-l)*((i-j-1)+(C[i]-C[j])-L));
//设k = i-1+C[i]-l
//对上述式子进行变形 可得:
//f[j]+(k-j-C[j])*(k-j-C[j]);f[j]+(k-(j+c[j]))*(k-(j+C[j]))
//f[i]=min(f[i],f[j]+(k-j-C[j])*(k-j-C[j]));
//设 x=j+C[j];
//f[i]=min(f[i],f[j]+(k-x)*(k-x));
//f[i]=min{f[j]+k^2-2KX+X^2}
//f[j]=f[i]-K^2+2KX-X^2;
//f[j]=f[i]-X^2+2KX-K^2;
//f[j]+X^2+K^2=f[i]+2KX;
//以 K为斜率 2X为横坐标那么f[i]为截距
//截距越小越好 所以采用单调队列这里 没有任何不合法K
//发现斜率应该是单调递增有可能不严格单调递增但是我们仍可以维护下凸壳
//此时 f[i]=f[q[h]]+X^2-2KX+K^2;
f[i]=f[q[l]]+k*k-2*k*x+x*x;
while(l<r&&(((f[q[r]]-f[q[r-1]]+y(r)*y(r)-y(r-1)*y(r-1))*2*(i+C[i]-q[r]-C[q[r]]))>=(((f[i]-f[q[r]]+(i+C[i])*(i+C[i])-y(r)*y(r))*2*(y(r)-y(r-1))))))r--;
q[++r]=i;
}
put(f[n]);
return 0;
}
View Code
这道题就比较有意思了 n^3 方的dp直接写啊
for(int i=1;i<=n;i++)
{
l=r=0;
for(int k=1;k<=i;k++)
{
while(l<r&&t[q[r]].y<=t[k].y)r--;
q[++r]=k;
}
//cout<<r<<endl;
for(int j=0;j<i;j++)
{
while(l<r&&q[l]<=j)l++;
f[i]=min(f[i],f[j]+(ll)t[q[l]].y*t[i].x);
}
}
put(f[n]);
View Code
这样可以愉快的得到60分 可是这让我非常的郁闷 因为这远远的偏离了正解
采用玩具装箱的斜率优化公式如果没有想到去重去掉不必要的土地的话 根本是不可能采用玩具装箱的优化方式的。
那么 只好看了一眼ppt 学长用另外一种方法进行了斜率优化。
//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
using namespace std;
char buf[1<<15],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline long long read()
{
long long x=0,f=1;char ch=getc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
return x*f;
}
inline void put(long long x)
{
x<0?x=-x,putchar('-'):0;
long long num=0;char ch[50];
while(x)ch[++num]=x%10+'0',x/=10;
num==0?putchar('0'):0;
while(num)putchar(ch[num--]);
putchar(10);return;
}
const ll MAXN=50002;
struct wy
{
ll x,y;
friend ll operator < (const wy x,const wy y)
{
if(x.x==y.x)return x.y<y.y;
return x.x<y.x;
}
}t[MAXN];
ll min(ll x,ll y){return x>y?y:x;}
ll n;
ll x[MAXN],y[MAXN],f[MAXN];
//f[i]表示前i个田地购买后的最小花费
//f[i]=min(f[i],f[j]+ymax(j+1~i)*x[i]);
//暴力求解n^3 貌似 可以单调队列稍稍优化一下 n^2
//n<=50000 还是过不了再次优化看到dp式子发现没有任何单调性可言
//优化不了了肿么办 考虑状态之间的关系
//设 对于i的某个状态 如果 u<v 且 V(u)>V(v)
//那么 f[u-1]+x[i]*y[u]>f[v-1]+x[i]*y[v]
//f[u-1]-f[v-1]>x[i]*(y[v]-y[u])
//(f[u-1]-f[v-1])/(y[v]-y[u])>x[i]
//因为 x[i]不断递增所以当出现这种情况时v点一定比u点更优
//从这个角度优化 打完后发现细节出现了点问题因为无法保证当前高度是最高的
//考虑去重或者去掉不必要的田地
ll q[MAXN],l=1,r,s[MAXN],h;
inline void discrete()
{
for(long long i=1;i<=n;i++)
{
while(h&&t[s[h]].y<=t[i].y)h--;
s[++h]=i;
}
return;
}
double k(long long p,long long q)
{
return ((1.0*(f[p-1]-f[q-1]))/(1.0*(y[p]-y[q])));
}
int main()
{
//freopen("1.in","r",stdin);
n=read();
for(ll i=1;i<=n;i++)t[i].x=read(),t[i].y=read();
sort(t+1,t+1+n);
discrete();
for(int i=1;i<=h;i++)f[i]=INF*1000000ll;
//put(h);
//for(long long i=1;i<=n;i++)cout<<t[i].x<<' '<<t[i].y<<endl;
for(ll i=1;i<=h;i++)x[i]=t[s[i]].x,y[i]=t[s[i]].y;
f[1]=x[1]*y[1];
q[++r]=1;
for(long long i=2;i<=h;i++)
{
while(l<r&&((f[q[l]-1]+x[i]*y[q[l]])>=(x[i]*y[q[l+1]]+f[q[l+1]-1])))l++;
f[i]=min(f[i],f[i-1]+y[i]*x[i]);
f[i]=min(f[i],f[q[l]-1]+y[q[l]]*x[i]);
while(l<r&&(k(q[r-1],q[r])<k(q[r],i)))--r;
q[++r]=i;
}
put(f[h]);
return 0;
}
View Code
//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
using namespace std;
char buf[1<<15],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline long long read()
{
long long x=0,f=1;char ch=getc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
return x*f;
}
inline void put(long long x)
{
x<0?x=-x,putchar('-'):0;
long long num=0;char ch[50];
while(x)ch[++num]=x%10+'0',x/=10;
num==0?putchar('0'):0;
while(num)putchar(ch[num--]);
putchar(10);return;
}
const ll MAXN=50002;
struct wy
{
ll x,y;
friend ll operator < (const wy x,const wy y)
{
if(x.x==y.x)return x.y<y.y;
return x.x<y.x;
}
}t[MAXN];
ll min(ll x,ll y){return x>y?y:x;}
ll n;
ll x[MAXN],y[MAXN],f[MAXN];
//f[i]表示前i个田地购买后的最小花费
//f[i]=min(f[i],f[j]+ymax(j+1~i)*x[i]);
//暴力求解n^3 貌似 可以单调队列稍稍优化一下 n^2
//n<=50000 还是过不了再次优化看到dp式子发现没有任何单调性可言
//优化不了了肿么办 考虑状态之间的关系
//设 对于i的某个状态 如果 u<v 且 V(u)>V(v)
//那么 f[u-1]+x[i]*y[u]>f[v-1]+x[i]*y[v]
//f[u-1]-f[v-1]>x[i]*(y[v]-y[u])
//(f[u-1]-f[v-1])/(y[v]-y[u])>x[i]
//因为 x[i]不断递增所以当出现这种情况时v点一定比u点更优
//从这个角度优化 打完后发现细节出现了点问题因为无法保证当前高度是最高的
//考虑去重或者去掉不必要的田地
//去掉没有价值的东西之后发现dp式子变成了
//f[i]=min(f[i],f[j-1]+y[j]*x[i]);
//又再次成为了可以斜率优化的式子 虽然上述方法也是可以的
//f[i]=f[j-1]+y[j]*x[i]; f[j-1]=f[i]-y[j]*x[i];
//这样的话x[i]为单调递增的斜率 -y[j]为 横坐标 f[j-1]为纵坐标
//使截距f[i]最小即可 那么此时我要让 -y[j]*x[i]越小越好
//换个说法因为-y[j]为负 那么我需要让y[j]*x[i]越大越好
//怎么说都和以前的不太一样因为这个地方竟然有个负号
//看了几张图之后发现和原来的近乎一样 维护一下就好了
ll q[MAXN],l=1,r,s[MAXN],h;
inline void discrete()
{
for(long long i=1;i<=n;i++)
{
while(h&&t[s[h]].y<=t[i].y)h--;
s[++h]=i;
}
return;
}
double k(long long u,long long v)
{
return ((f[u-1]-f[v-1])*1.0)/(1.0*(-y[u]+y[v]));
}
int main()
{
//freopen("1.in","r",stdin);
n=read();
for(ll i=1;i<=n;i++)t[i].x=read(),t[i].y=read();
sort(t+1,t+1+n);
discrete();
for(long long i=1;i<=h;i++)f[i]=INF*1000000ll;
//put(h);
//for(long long i=1;i<=n;i++)cout<<t[i].x<<' '<<t[i].y<<endl;
for(ll i=1;i<=h;i++)x[i]=t[s[i]].x,y[i]=t[s[i]].y;
f[1]=x[1]*y[1];
q[++r]=1;
for(long long i=2;i<=h;i++)
{
while(l<r&&k(q[l+1],q[l])<x[i])++l;
f[i]=min(x[i]*y[i]+f[i-1],f[q[l]-1]+y[q[l]]*x[i]);
while(l<r&&k(q[r-1],q[r])>k(q[r],i))--r;
q[++r]=i;
}
put(f[h]);
return 0;
}