搬讲义~~~~

题目1:玩具装箱(bzoj1010)

Description

P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为1…N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一个容器中,那么容器的长度将为 x=j-i+Sigma(Ck) i<=K<=j 制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为x,其制作费用为(X-L)^2.其中L是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过L。但他希望费用最小.

Input

第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7

Output

输出最小费用

Sample Input

5 4
3
4
2
1
4

Sample Output

1
 
经典的决策单调性问题··但也可以用斜率优化····维护一个栈,栈中维护每个决策以及用到它的最优状态的左右区间··每次先更新状态然后将其作为决策二分查找替换栈中决策即可···
另外由于决策单调性是建立在平行四边形不等式的基础上的··而那个东西证起来很麻烦····做题的时候也不容易反应过来··所以尽量还是打表来寻找决策单调性··
 
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=5e4+;
long long sum[N],f[N];
struct node
{
int l,r,pos;
}que[N];
int n,L,head,tail;
inline int R()
{
char c;int f=;
for(c=getchar();c<''||c>'';c=getchar());
for(;c<=''&&c>='';c=getchar()) f=(f<<)+(f<<)+c-'';
return f;
}
inline long long get(int j,int i)
{
return f[j]+(long long)(sum[i]-sum[j]+i-j--L)*(sum[i]-sum[j]+i-j--L);
}
inline int find(node a,int b)
{
int le=a.l,ri=a.r;
while(le<=ri)
{
int mid=(le+ri)/;
if(get(b,mid)<get(a.pos,mid)) ri=mid-;
else le=mid+;
}
return le;
}
inline void work()
{
head=,tail=;
node temp;temp.l=,temp.r=n,temp.pos=;que[++tail]=temp;
for(int i=;i<=n;i++)
{
while(que[head].r<i) head++;
f[i]=get(que[head].pos,i);
if(head>tail||get(i,n)<get(que[tail].pos,n))
{
while(head<=tail&&(get(i,que[tail].l)<get(que[tail].pos,que[tail].l))) tail--;
if(head<=tail)
{
int t=find(que[tail],i);
que[tail].r=t-;
node temp;temp.l=t;temp.r=n;temp.pos=i;
que[++tail]=temp;
}
else
{
node temp;temp.l=i,temp.r=n,temp.pos=i;
que[++tail]=temp;
}
}
}
}
int main()
{
//freopen("a.in","r",stdin);
n=R();L=R();
for(int i=;i<=n;i++) sum[i]=R(),sum[i]+=sum[i-];
work();cout<<f[n]<<endl;
return ;
}

题目2:土地购买(bzoj1597)

Description

农夫John准备扩大他的农场,他正在考虑N (1 <= N <= 50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <= 1,000,000; 1 <= 长 <= 1,000,000). 每块土地的价格是它的面积,但FJ可以同时购买多快土地. 这些土地的价格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换. 如果FJ买一块3x5的地和一块5x3的地,则他需要付5x5=25. FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费. 他需要你帮助他找到最小的经费.

Input

* 第1行: 一个数: N

* 第2..N+1行: 第i+1行包含两个数,分别为第i块土地的长和宽

Output

* 第一行: 最小的可行费用.

Sample Input

4
100 1
15 15
20 5
1 100

输入解释:

共有4块土地.

Sample Output

500

HINT

FJ分3组买这些土地: 第一组:100x1, 第二组1x100, 第三组20x5 和 15x15 plot. 每组的价格分别为100,100,300, 总共500.

这道题首先将土地按x降序排序···可以容易发现那些x和y都会小于某一个土地的土地是肯定是多余无用的···所以将这些点排除后剩余土地肯定是按照x降序··y升序排列的,于是可以推出dp方程

其中f[n]表示的是购买前n块土地的最小花费···方程满足单调性····于是就和上面一道题一样了······其实这道题也可以用斜率优化来做·····后面会提到

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=5e4+;
struct node
{
int x,y;
}a[N],b[N];
struct node1
{
int l,r,pos;
}que[N];
inline bool cmp(node a,node b)
{
if(a.x==b.x) return a.y>b.y;
return a.x>b.x;
}
inline int R()
{
char c;int f=;
for(c=getchar();c<''||c>'';c=getchar());
for(;c<=''&&c>='';c=getchar()) f=(f<<)+(f<<)+c-'';
return f;
}
int n,tot;
long long f[N];
inline long long calc(int i,int j)
{
return f[j]+(long long)b[i].y*b[j+].x;
}
inline int find(node1 a,int b)
{
int le=a.l,ri=a.r,ans=a.r+;
while(le<=ri)
{
int mid=(le+ri)/;
if(calc(mid,b)<calc(mid,a.pos)) ri=mid-,ans=mid;
else le=mid+;
}
return ans;
}
inline bool dp()
{
int head=,tail=;
node1 temp;temp.l=;temp.r=tot;temp.pos=;que[++tail]=temp;
for(int i=;i<=tot;i++)
{
while(que[head].r<i) head++;
f[i]=calc(i,que[head].pos);
if(calc(tot,i)<calc(tot,que[tail].pos))
{
while(head<=tail&&calc(que[tail].l,i)<calc(que[tail].l,que[tail].pos)) tail--;
if(head<=tail)
{
int t=find(que[tail],i);
que[tail].r=t-;
node1 temp;temp.l=t,temp.r=tot,temp.pos=i;que[++tail]=temp;
}
else
{
node1 temp;temp.l=i;temp.r=tot;temp.pos=i;que[++tail]=temp;
}
}
}
}
int main()
{
// freopen("a.in","r",stdin);
n=R();
for(int i=;i<=n;i++)
a[i].x=R(),a[i].y=R();
sort(a+,a+n+,cmp);b[++tot]=a[];int maxx=a[].y;
for(int i=;i<=n;i++)
if(a[i].y>maxx) maxx=a[i].y,b[++tot]=a[i];
dp();cout<<f[tot]<<endl;
return ;
}
 

题目1:生产产品(vijo1243)

描述

在经过一段时间的经营后,dd_engi的OI商店不满足于从别的供货商那里购买产品放上货架,而要开始自己生产产品了!产品的生产需要M个步骤,每一个步骤都可以在N台机器中的任何一台完成,但生产的步骤必须严格按顺序执行。由于这N台机器的性能不同,它们完成每一个步骤的所需时间也不同。机器i完成第j个步骤的时间为T[i,j]。把半成品从一台机器上搬到另一台机器上也需要一定的时间K。同时,为了保证安全和产品的质量,每台机器最多只能连续完成产品的L个步骤。也就是说,如果有一台机器连续完成了产品的L个步骤,下一个步骤就必须换一台机器来完成。现在,dd_engi的OI商店有史以来的第一个产品就要开始生产了,那么最短需要多长时间呢?
某日Azuki.7对跃动说:这样的题目太简单,我们把题目的范围改一改
对于菜鸟跃动来说,这是个很困难的问题,他希望你能帮他解决这个问题

格式

输入格式

第一行有四个整数M, N, K, L
下面的N行,每行有M个整数。第I+1行的第J个整数为T[J,I]。

输出格式

输出只有一行,表示需要的最短时间。

样例1

样例输入1

3 2 0 2
2 2 3
1 3 1

样例输出1

4

限制

1s

提示

对于50%的数据,N<=5,L<=4,M<=10000
对于100%的数据,N<=5, L<=50000,M<=100000

来源

第一届“OI商店杯” dd_engi原创题目

很妙的一道单调队列的题···,之前其实是做过的···

首先容易想到朴素的dp方程:f[i][j]表示第i个机器生产了第j个过程且前j个过程已经完成生产的最小花费···容易得到:

dp[i][j]=min{dp[k][j']+sum[i][j]-sum[i][j']}+K

其中sum为预处理出的前缀和···j-l<j'<j

由此我们将sum[i][j]提到外面后我们不难发现大括号号中的部分实际上是关于j'的一个函数····因此可以按照上述步骤用单调队列解决:

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
#include<deque>
using namespace std;
const int M=1e5+;
const int N=;
const int inf=0x7f7f7f7f;
deque<int> dque[N][N];
int n,m,K,sum[M][N],l,dp[M][N];
inline int R()
{
char c;int f=;
for(c=getchar();c<''||c>'';c=getchar());
for(;c<=''&&c>='';c=getchar())
f=(f<<)+(f<<)+c-'';
return f;
}
inline int calc(int i,int k,int j)
{
return dp[k][j]-sum[k][i];
}
int main()
{
m=R(),n=R(),K=R(),l=R();
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
sum[j][i]=R(),sum[j][i]+=sum[j-][i];
memset(dp,inf,sizeof(dp));
for(int i=;i<=n;i++) dp[][i]=;
for(int j=; j<=m; ++j)
{
for(int i=; i<=n; ++i)
for(int k=; k<=n; ++k)
if(i!=k) while(!dque[i][k].empty()&&(j-dque[i][k].front())>l) dque[i][k].pop_front();
for(int i=; i<=n; ++i)
for(int k=; k<=n; ++k)
if(i!=k)
{
int a=,b=;
if(!dque[i][k].empty()) a=calc(i,dque[i][k].front(),k);
b=sum[j][i]+K;
dp[j][i]=min(dp[j][i],a+b);
}
for(int i=; i<=n; ++i)
for(int k=; k<=n; ++k)
if(i!=k)
{
while(!dque[i][k].empty()&&(calc(i,dque[i][k].back(),k)>=calc(i,j,k))) dque[i][k].pop_back();
dque[i][k].push_back(j);
}
}
int ans=inf;
for(int i=;i<=n;i++) ans=min(ans,dp[m][i]);
cout<<ans-K<<endl;
return ;
}

题目1:Max Sum Plus Plus(hdu1024)

Problem Description

Now I think you have got an AC in Ignatius.L's "Max Sum" problem. To be a brave ACMer, we always challenge ourselves to more difficult problems. Now you are faced with a more difficult problem.

Given a consecutive number sequence S1, S2, S3, S4 ... Sx, ... Sn (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ Sx ≤ 32767). We define a function sum(i, j) = Si + ... + Sj (1 ≤ i ≤ j ≤ n).

Now given an integer m (m > 0), your task is to find m pairs of i and j which make sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + ... + sum(im, jm) maximal (ix ≤ iy ≤ jx or ix ≤ jy ≤ jx is not allowed).

But I`m lazy, I don't want to write a special-judge module, so you don't have to output m pairs of i and j, just output the maximal summation of sum(ix, jx)(1 ≤ x ≤ m) instead. ^_^

Input

Each test case will begin with two integers m and n, followed by n integers S1, S2, S3 ... Sn.
Process to the end of file.

Output

Output the maximal summation described above in one line.

Sample Input

1 3 1 2 3
2 6 -1 4 -2 3 -2 3

Sample Output

6 8

Hint

Huge input, scanf and dynamic programming is recommended.

 
用f[i][j]表示取了i段且第i段最后一个数取的是num[j]时最大的和···推出朴素dp方程:

f[i][j]=max{f[i][j-1]+num[j],f[i-1][k]+num[j]}

其中k小于j·····

不难发现每当我们先枚举i再枚举j时此时的f[i][j-1]只与上一层i-1有关,所以我们可以降维··用f[j]来代表此时的f[i][j]那么可以推出dp方程

f[j]=max(f[j-1]+num[j],temp[j-1]+num[j]);

其中temp为在枚举i-1时已经处理出的上一层的最小值

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+;
const int inf=0x3f3f3f3f;
inline int R()
{
char c;int f=,i=;
for(c=getchar();(c<''||c>'')&&c!='-';c=getchar());
if(c=='-') i=-,c=getchar();
for(;c<=''&&c>='';c=getchar()) f=(f<<)+(f<<)+c-'';
return f*i;
}
int num[N],n,m;
long long f[N],temp[N];
int main()
{
//freopen("a.in","r",stdin);
while(scanf("%d%d",&m,&n)!=EOF)
{
memset(temp,,sizeof(temp));
memset(f,,sizeof(f));
for(int i=;i<=n;i++) num[i]=R();
f[]=temp[]=;
for(int i=;i<=m;i++)
{
long long maxx=-inf;
for(int j=i;j<=n;j++)
{
f[j]=max(f[j-]+num[j],temp[j-]+num[j]);
temp[j-]=maxx;maxx=max(maxx,f[j]);
}
}
long long ans=-inf;
for(int i=m;i<=n;i++) ans=max(ans,f[i]);
cout<<ans<<endl;
}
return ;
}

题目2:Max Sum Plus Plus Plus

Problem Description

给定一个由n个正整数组成的整数序列

a1 a2 a3 ... an

求按先后次序在其中取m段长度分别为l1、l2、l3...lm的不交叠的连续整数的和的最大值。

Input

第一行是一个整数n(0 ≤ n ≤ 1000),n = 0表示输入结束
第二行的第一个数是m(1 ≤ m ≤ 20),
第二行接下来有m个整数l1,l2...lm。
第三行是n个整数a1, a2, a2 ... an.

Output

输出m段整数和的最大值。

Sample Input

3
2 1 1
1 2 3
4
2 1 2
1 2 3 5
0

Sample Output

5
10

和上面一道题几乎是差不多的···只是注意此时f[i][j]表示的是取第i段,第i段的最后一个数字是num[j]时的最大价值··

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cctype>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int N=;
const int M=;
int numn[N],numm[M],n,m;
long long sumn[N],summ[M],f[N],maxx[N];
inline int R()
{
char c;int f=,i=;
for(c=getchar();(c<''||c>'')&&c!='-';c=getchar());
if(c=='-') i=-,c=getchar();
for(;c<=''&&c>='';c=getchar()) f=(f<<)+(f<<)+c-'';
return f*i;
}
int main()
{
//freopen("a.in","r",stdin);
while(true)
{
n=R();if(!n) break;m=R();
memset(sumn,,sizeof(sumn));memset(summ,,sizeof(summ));
memset(f,,sizeof(f));memset(maxx,,sizeof(maxx));
for(int i=;i<=m;i++) numm[i]=R(),summ[i]=numm[i]+summ[i-];
for(int i=;i<=n;i++) numn[i]=R(),sumn[i]=numn[i]+sumn[i-];
for(int i=;i<=m;i++)
{
for(int j=summ[i];j<=n;j++)
{
if(j==summ[i]) f[j]=sumn[j];
else f[j]=maxx[j-numm[i]]+sumn[j]-sumn[j-numm[i]];
}
for(int j=summ[i];j<=n;j++)
{
if(j==summ[i]) maxx[j]=f[j];
else maxx[j]=max(maxx[j-],f[j]);
}
}
long long anss=-1e+;
for(int i=summ[m];i<=n;i++) anss=max(anss,f[i]);
cout<<anss<<endl;
}
return ;
}

算法复习——1D/1Ddp优化的更多相关文章

  1. C#冒泡算法复习

    C#冒泡算法复习 冒泡算法的意思:每一趟找到一个最小或最大的数放到最后面,比较总数的n-1次(因为比较是2个双双比较的) 第一层循环表示进行比较的次数,总共要比较(数的)-1次 (因为比较是2个双双比 ...

  2. 谷歌蜂鸟算法对网站seo优化有何影响

    http://www.wocaoseo.com/thread-89-1-1.html       谷歌在过去三个月里,非常低调的推出了蜂鸟算法,据谷歌技术员表示,此种方法一出,将影响90%网站的排名, ...

  3. C语言排序算法复习

    排序算法有很多种,这里在复习和分析的基础上,做一个自己的总结: 首先要知道有哪些排序算法,google一下,有云C语言7大经典排序算法(也有8大).主要包括冒泡排序,快速排序,选择排序,插入排序,希尔 ...

  4. [Algorithm] 群体智能优化算法之粒子群优化算法

    同进化算法(见博客<[Evolutionary Algorithm] 进化算法简介>,进化算法是受生物进化机制启发而产生的一系列算法)和人工神经网络算法(Neural Networks,简 ...

  5. K-Means聚类和EM算法复习总结

    摘要: 1.算法概述 2.算法推导 3.算法特性及优缺点 4.注意事项 5.实现和具体例子 6.适用场合 内容: 1.算法概述 k-means算法是一种得到最广泛使用的聚类算法. 它是将各个聚类子集内 ...

  6. 揽货最短路径解决方案算法 - C# 蚁群优化算法实现

    需求为(自己编的,非实际项目): 某配送中心进行揽货,目标客户数为50个客户,配送中心目前的运力资源如下: 现有车辆5台 单台运力最大行驶距离200千米 单台运力最大载重公斤1吨 问:运力怎样走法才能 ...

  7. KMP算法复习【+继续学习】

    离NOIP还剩12天,本蒟蒻开始准备复习了. 先来个KMP[似乎我并没有写过KMP的blog] KMP KMP算法是解决字符串匹配问题的一个算法,主要是单对单的字符串匹配加速,时间复杂度O(m + n ...

  8. DJ 算法的队列优先优化

    DJ算法就是求单源最短路的算法,但是时间复杂度不太理想,所以在此献上用最小堆来优化的算法. 如果不懂优先队列可以先去看STL分类关于优先队列的介绍: ///POJ 2387为例 #include< ...

  9. 近期公共祖先(LCA)——离线Tarjan算法+并查集优化

    一. 离线Tarjan算法 LCA问题(lowest common ancestors):在一个有根树T中.两个节点和 e&sig=3136f1d5fcf75709d9ac882bd8cfe0 ...

随机推荐

  1. ReferenceError: internalBinding is not defined

    ReferenceError: internalBinding is not defined at internal/util/inspect.js:31:15 at req_ (D:\workspa ...

  2. 导入文件 服务器报错,有可能是 开发时候是window 服务器是linux,两个系统的文件系统的/和\是相反的,要注意这块

    导入文件 服务器报错,有可能是 开发时候是window 服务器是linux,两个系统的文件系统的/和\是相反的,要注意这块

  3. 查看nvidia的GPU

    nvidia-smi就可以查看,可以看到进程的占用率,可以kill杀掉进程 注意这里的-前后都不要空格,连起来写

  4. c++作业:输入两个整数,用函数求两数之和。函数外部声明有什么作用?

    #include <iostream> using namespace std; int main(){ //求两数的和? int a,b,s; cout<<"请你输 ...

  5. 禅与 Objective-C 编程艺术(Zen and the Art of the Objective-C Craftsmanship)

    英文版Zen and the Art of the Objective-C Craftsmanshiphttps://github.com/objc-zen/objc-zen-book 中文版禅与 O ...

  6. 如何在 JavaScript 中更好地使用数组

    使用 Array.includes 替代 Array.indexOf “如果需要在数组中查找某个元素,请使用 Array.indexOf.” 我记得在我学习 JavaScript 的课程中有类似的这么 ...

  7. c++ 用指针操作数组

    #include <iostream> using namespace std; const int Max = 5; double * fill_array(double * first ...

  8. 洛谷P2347 砝码称重

    题目 貌似是某年提高组签到题,六重循环零压力AC,差点怒踩std 但本蒟蒻决定写正解——多重背包,果断20分 原因是写错了状态转移方程...神才知道我咋过的样例和两个测试点 扯远了 多重背包 简单说一 ...

  9. PHP 日常开发过程中的bug集合(持续更新中。。。)

    PHP 日常开发过程中的bug集合(持续更新中...) 在日常php开发过程中,会遇到一些意想不到的bug,所以想着把这些bug记录下来,以免再犯! 1.字符串 '0.00'.'0.0'.'0'  是 ...

  10. 【js】input 焦点到内容的最后

     //引用部分应支持jQuery  function find_focus(obj){    var curr = jQuery(obj);    var val = curr.val();    c ...