今天一天8h 写了两道斜率优化的题(别问我效率为什么这么低 代码bug太多了)

关键是思考的不周全 估计是写的题少手生 以后就会熟练起来了吧。

这道题显然有一个n^2的dp方程

设f[i]表示前i件物品放好的最小费用   f[i]=min(f[i],f[j]+((i-j-1)*(Ci-Cj)-L)^2);

那么这样我们可以愉快的得到了30分但我的常数比较大 是20分。

针对上述式子我们可以将其变形 把和i相关的都放到一边 因为此时和i相关的除了f[i]都是定值

//对上述式子进行变形 可得:

//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;

然后 双端队列维护一下注意一下细节即可得出答案 亲测不会爆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[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline long long read()
{
long long x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(long long x)
{
x<?x=-x,putchar('-'):;
long long num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();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=;
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=;i<=n;i++)
{
C[i]=read();
C[i]+=C[i-];
}
for(long long i=;i<=n;i++)f[i]=INF;
f[]=;q[++r]=;l=r;
for(long long i=;i<=n;i++)
{
long long k=i-+C[i]-L;
while(l<r&&(f[q[l+]]-f[q[l]]+y(l+)*y(l+)-y(l)*y(l)<=*k*(y(l+)-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-*k*x+x*x;
while(l<r&&(((f[q[r]]-f[q[r-]]+y(r)*y(r)-y(r-)*y(r-))**(i+C[i]-q[r]-C[q[r]]))>=(((f[i]-f[q[r]]+(i+C[i])*(i+C[i])-y(r)*y(r))**(y(r)-y(r-))))))r--;
q[++r]=i;
}
put(f[n]);
return ;
}

这道题就比较有意思了 n^3 方的dp直接写啊

//f[i]表示前i个田地购买后的最小花费  f[i]=min(f[i],f[j]+ymax(j+1~i)*x[i]);

机智的我知道 搞一个单调队列优化似乎可以得到n^2优秀复杂度

for(int i=;i<=n;i++)
{
l=r=;
for(int k=;k<=i;k++)
{
while(l<r&&t[q[r]].y<=t[k].y)r--;
q[++r]=k;
}
//cout<<r<<endl;
for(int j=;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]);

这样可以愉快的得到60分 可是这让我非常的郁闷 因为这远远的偏离了正解

采用玩具装箱的斜率优化公式如果没有想到去重去掉不必要的土地的话 根本是不可能采用玩具装箱的优化方式的。

那么 只好看了一眼ppt 学长用另外一种方法进行了斜率优化。

设 对于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式子进行改变 这样也是可以的。

所以说两种方法其实是殊途同归了非常的巧妙!

值得一提的是可以开多个函数使自己冗杂的程序变得简明 或者交叉乘的时候换成double 再除法 这样可以避免一些问题如爆longlong

最后细节真的很重要这道题我调了6h 才调出来,好在对斜率优化的感触非常深!

//#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[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline long long read()
{
long long x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(long long x)
{
x<?x=-x,putchar('-'):;
long long num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
const ll MAXN=;
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=,r,s[MAXN],h;
inline void discrete()
{
for(long long i=;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-]-f[q-]))/(1.0*(y[p]-y[q])));
}
int main()
{
//freopen("1.in","r",stdin);
n=read();
for(ll i=;i<=n;i++)t[i].x=read(),t[i].y=read();
sort(t+,t++n);
discrete();
for(int i=;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=;i<=h;i++)x[i]=t[s[i]].x,y[i]=t[s[i]].y;
f[]=x[]*y[];
q[++r]=;
for(long long i=;i<=h;i++)
{
while(l<r&&((f[q[l]-]+x[i]*y[q[l]])>=(x[i]*y[q[l+]]+f[q[l+]-])))l++;
f[i]=min(f[i],f[i-]+y[i]*x[i]);
f[i]=min(f[i],f[q[l]-]+y[q[l]]*x[i]);
while(l<r&&(k(q[r-],q[r])<k(q[r],i)))--r;
q[++r]=i;
}
put(f[h]);
return ;
}

用了另一种方法重写了一遍真的是殊途同归 经典优化。这次我只花了20min(个人感觉第一种更舒服一点)

//去掉没有价值的东西之后发现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]越大越好
//怎么说都和以前的不太一样因为这个地方竟然有个负号
//看了几张图之后发现和原来的近乎一样 维护一下就好了

对于点在负半轴上我只需要 还是维护两点斜率比当前斜率大的部分即可。

还是维护一个下凸壳没了。

//#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[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline long long read()
{
long long x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(long long x)
{
x<?x=-x,putchar('-'):;
long long num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
const ll MAXN=;
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=,r,s[MAXN],h;
inline void discrete()
{
for(long long i=;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-]-f[v-])*1.0)/(1.0*(-y[u]+y[v]));
}
int main()
{
//freopen("1.in","r",stdin);
n=read();
for(ll i=;i<=n;i++)t[i].x=read(),t[i].y=read();
sort(t+,t++n);
discrete();
for(long long i=;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=;i<=h;i++)x[i]=t[s[i]].x,y[i]=t[s[i]].y;
f[]=x[]*y[];
q[++r]=;
for(long long i=;i<=h;i++)
{
while(l<r&&k(q[l+],q[l])<x[i])++l;
f[i]=min(x[i]*y[i]+f[i-],f[q[l]-]+y[q[l]]*x[i]);
while(l<r&&k(q[r-],q[r])>k(q[r],i))--r;
q[++r]=i;
}
put(f[h]);
return ;
}

对于这两道题总结一下犯错误的地方 :

1 细节打错的比较多稍加一对括号什么的 多打-1少打+1的地方需注意 不能很马虎。要严谨。

2 双端队列进入的时候需要先把队尾的比较出来再加进去 例如上一题的状态的缺漏之处 我理所当然的写错了。

3 分析斜率优化时对于点的分布 的问题一定仔细分析怎么搞才是最优的

4 对于一些计算斜率的尽量使用函数来简化自己的程序 不要太过冗杂

不可能这三个字,你说的太多了

玩具装箱&土地购买的更多相关文章

  1. usaco 土地并购 && hdu 玩具装箱

    土地并购: Description 约翰准备扩大他的农场,眼前他正在考虑购买N块长方形的土地.如果约翰单买一块土地,价格就是土地的面积.但他可以选择并购一组土地,并购的价格为这些土地中最大的长乘以最大 ...

  2. BZOJ1010:[HNOI2008]玩具装箱——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=1010 P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行 ...

  3. BZOJ 1597: [Usaco2008 Mar]土地购买 [斜率优化DP]

    1597: [Usaco2008 Mar]土地购买 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4026  Solved: 1473[Submit] ...

  4. BZOJ 1010: [HNOI2008]玩具装箱toy [DP 斜率优化]

    1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 9812  Solved: 3978[Submit][St ...

  5. 1597: [Usaco2008 Mar]土地购买

    1597: [Usaco2008 Mar]土地购买 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4023  Solved: 1470[Submit] ...

  6. 【BZOJ-1010】玩具装箱toy DP + 斜率优化

    1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 8432  Solved: 3338[Submit][St ...

  7. 【BZOJ-1597】土地购买 DP + 斜率优化

    1597: [Usaco2008 Mar]土地购买 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2931  Solved: 1091[Submit] ...

  8. C++之路进阶——codevs1319(玩具装箱)

    1319 玩具装箱  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond     题目描述 Description P教授要去看奥运,但是他舍不下他的玩具,于是 ...

  9. BZOJ 1010: [HNOI2008]玩具装箱toy 斜率优化DP

    1010: [HNOI2008]玩具装箱toy Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再 ...

随机推荐

  1. 超级NB的防DDOS(小量级)攻击的脚本

    # tree /usr/local/ddos/ /usr/local/ddos/ ├── ddos.conf ├── ddos.sh ├── ignore.ip.list └── LICENSE di ...

  2. 【Java】浅谈HashMap

    HashMap是常用的集合类,以Key-Value形式存储值.下面一起从代码层面理解它的实现. 构造方法 它有好几个构造方法,但几乎都是调此构造方法: public HashMap(int initi ...

  3. 【九天教您南方cass 9.1】 05 打印出图

    同学们大家好,欢迎收看由老王测量上班记出品的cass9.1视频课程 我是本节课主讲老师九天. 我们讲课的教程附件也是共享的,请注意索取测量空间中. [点击索取cass教程]5元立得 (给客服说暗号:“ ...

  4. 【iCore1S 双核心板_FPGA】例程一:GPIO输出实验——点亮LED

    实验现象: 三色LED循环点亮. 核心源代码: //--------------------Module_LED-----------------------------// module LED( ...

  5. 【iCore1S 双核心板_FPGA】例程十六:基于SPI的ARM与FPGA通信实验

    实验现象: 核心代码: int main(void) { int i,n; ]; ]; HAL_Init(); system_clock.initialize(); led.initialize(); ...

  6. postman中 form-data、x-www-form-urlencoded、raw、binary的区别--转

    原文地址:http://blog.csdn.net/ye1992/article/details/49998511 1.form-data:  就是http请求中的multipart/form-dat ...

  7. win7下安装Office2010老是出现提示安装MSXML6.10.1129.0,下载官方MSXML后提示安装成功却也安装不了

    在注册表中增加以下信息: [HKEY_CLASSES_ROOT\TypeLib\{F5078F18-C551-11D3-89B9-0000F81FE221}][HKEY_CLASSES_ROOT\Ty ...

  8. 公众平台返回原始数据为: 错误代码-40164,错误信息-invalid ip, not in whitelist hint

    1.登录公众平台,进入开发->基本配置页面 2.点击配置进入IP白名单设置页 3.填写微客助理IP地址:183.63.25.68 4.管理员扫码确认保存 5.设置成功后点击“关闭” 6.修改成功 ...

  9. EventFlow.helper.js 事件流程控制

    /*! * 事件流程管理 * version: 1.0.0-2018.07.25 * Requires ES6 * Copyright (c) 2018 Tiac * http://www.cnblo ...

  10. iOS开发-- 一个苹果证书如何多次使用

    苹果的开发者账号限制开发者证书只能有5个,我们开发过程中遇到超过5个人需要真机调试的情况,如何解决这个问题呢? 有两种方式可以解决问题: 1. Revoke原来的证书----不推荐 将以前的证书“re ...