大视野在线测评 1010 [HNOI2008]玩具装箱toy (动态规划)
2015-06-05 19:43
507 查看
题目链接
162 MB
Submit: 7027 Solved: 2670
[Submit][Status][Discuss]
3
4
2
1
4
题解:用dp[i]表示装完前i个物品的最小花费,转移就是:
dp[i]=min(dp[k]+(sum[i]-sum[k]+i-k-1-l)*(sum[i]-sum[k]+i-k-1-l))
sum[i]表示前i个物品的前缀和。
方法一:
另w(k,i)=(sum[i]-sum[k]+i-k-1-l)*(sum[i]-sum[k]+i-k-1-l);
可以证明:w(i,j)+w(i+1,j+1)<=w(i,j+1)+w(i+1,j)。
另k(i)表示状态i取到最优值时的决策,则:k(i)<=k(j)。决策单调。
因此我们可以采用论文:《1D/1D动态规划优化初步》中的方法。
O(nlgn)解决此问题。代码如下:
方法二:再来分析转移方程:
dp[i]=min(dp[k]+(sum[i]-sum[k]+i-k-1-l)*(sum[i]-sum[k]+i-k-1-l))
=min(dp[k]+(sum[i]+i-l-1)*(sum[i]+i-l-1)+(sum[k]+k)*(sum[k]+k)-2*(sum[i]+i-l-1)*(sum[k]+k))
=min( dp[k]+(sum[k]+k)*(sum[k]+k)-2*(sum[i]+i-l-1)*(sum[k]+k) ) +
(sum[i]+i-l-1)*(sum[i]+i-l-1)
我们令常量 P=(sum[i]+i-l-1)
令:X(k)=dp[k]+(sum[k]+k)*(sum[k]+k)
令:Y(k)=(sum[k]+k)
则:dp[i]=min(X(k)-2*P*Y(k))+p*p;
令C=X(k)-2*P*Y(k) ,则:
Y(k)=1/2p*X(k) - C/2*p;
我们可以把决策K看成是二维坐标上的一个点( X(k),Y(k) ) ,求解dp[i]可以看成是,一条斜率固定的直线,在一些点中(决策),找一个点,使得纵坐标的截距最大即(C最小)。不难发现,这个点一定在这些点的凸包上。
由于1/2*p随着i的增加单调递减,X(k)随着K的增加单调递增。
因此我们可以用一个双端队列维护凸包上的点,队列中相邻两点的斜率随着x的增加而减少。
每次转移的时候,从队首开始,找到第一个与后一个点的斜率小于当前直线的斜率的点,就是我们要找的决策点。对于不满足条件的点出队。
转移完成以后,从队尾开始添加一个点,保证队列中的点斜率递减,不满足条件的点出队。
这样就可以均摊O(1)复杂度,实现转移。
对于斜率优化的具体内容可看论文:《1D/1D动态规划优化初步》,或者其它关于斜率优化的论文。
代码如下:
1010: [HNOI2008]玩具装箱toy
Time Limit: 1 Sec Memory Limit:162 MB
Submit: 7027 Solved: 2670
[Submit][Status][Discuss]
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^7Output
输出最小费用Sample Input
5 43
4
2
1
4
Sample Output
1题解:用dp[i]表示装完前i个物品的最小花费,转移就是:
dp[i]=min(dp[k]+(sum[i]-sum[k]+i-k-1-l)*(sum[i]-sum[k]+i-k-1-l))
sum[i]表示前i个物品的前缀和。
方法一:
另w(k,i)=(sum[i]-sum[k]+i-k-1-l)*(sum[i]-sum[k]+i-k-1-l);
可以证明:w(i,j)+w(i+1,j+1)<=w(i,j+1)+w(i+1,j)。
另k(i)表示状态i取到最优值时的决策,则:k(i)<=k(j)。决策单调。
因此我们可以采用论文:《1D/1D动态规划优化初步》中的方法。
O(nlgn)解决此问题。代码如下:
#include<stdio.h> #include<iostream> #include<algorithm> #include<map> #include<string.h> #include<vector> #include<math.h> typedef long long LL; typedef unsigned long long LLU; const double eps=1e-8; const int nn=110000; const int inf=0x3fffffff; using namespace std; int n; LL l,c[nn]; LL dp[nn]; LL sum[nn]; struct node { int x,y; int val; node(){} node(int xx,int yy,int vall) { x=xx,y=yy,val=vall; } }; node que[nn]; int head,last; LL solve(int i,int k) { return dp[k]+(sum[i]-sum[k]+i-k-1-l)*(sum[i]-sum[k]+i-k-1-l); } bool check(int id,int v1,int v2) { return solve(id,v1)<solve(id,v2); } void add(int x,int y,int val) { node tem=node(x,y,val); while(last>head) { if(check(que[last-1].x,que[last-1].val,val)) { int l=que[last-1].x+1,r=que[last-1].y+1; while(l<r) { int mid=(l+r)/2; if(check(mid,que[last-1].val,val)) { l=mid+1; } else r=mid; } tem.x=r; que[last-1].y=r-1; break; } else { tem.x=que[last-1].x; last--; } } if(tem.x<=n) { tem.y=n; que[last++]=tem; } } int main() { int i; while(scanf("%d%lld",&n,&l)!=EOF) { sum[0]=0; for(i=1;i<=n;i++) { scanf("%lld",&c[i]); sum[i]=sum[i-1]+c[i]; } dp[0]=0; head=last=0; add(1,n,0); for(i=1;i<=n;i++) { while(que[head].y<i) { head++; } dp[i]=solve(i,que[head].val); add(n+1,n+1,i); } printf("%lld\n",dp ); } return 0; }
方法二:再来分析转移方程:
dp[i]=min(dp[k]+(sum[i]-sum[k]+i-k-1-l)*(sum[i]-sum[k]+i-k-1-l))
=min(dp[k]+(sum[i]+i-l-1)*(sum[i]+i-l-1)+(sum[k]+k)*(sum[k]+k)-2*(sum[i]+i-l-1)*(sum[k]+k))
=min( dp[k]+(sum[k]+k)*(sum[k]+k)-2*(sum[i]+i-l-1)*(sum[k]+k) ) +
(sum[i]+i-l-1)*(sum[i]+i-l-1)
我们令常量 P=(sum[i]+i-l-1)
令:X(k)=dp[k]+(sum[k]+k)*(sum[k]+k)
令:Y(k)=(sum[k]+k)
则:dp[i]=min(X(k)-2*P*Y(k))+p*p;
令C=X(k)-2*P*Y(k) ,则:
Y(k)=1/2p*X(k) - C/2*p;
我们可以把决策K看成是二维坐标上的一个点( X(k),Y(k) ) ,求解dp[i]可以看成是,一条斜率固定的直线,在一些点中(决策),找一个点,使得纵坐标的截距最大即(C最小)。不难发现,这个点一定在这些点的凸包上。
由于1/2*p随着i的增加单调递减,X(k)随着K的增加单调递增。
因此我们可以用一个双端队列维护凸包上的点,队列中相邻两点的斜率随着x的增加而减少。
每次转移的时候,从队首开始,找到第一个与后一个点的斜率小于当前直线的斜率的点,就是我们要找的决策点。对于不满足条件的点出队。
转移完成以后,从队尾开始添加一个点,保证队列中的点斜率递减,不满足条件的点出队。
这样就可以均摊O(1)复杂度,实现转移。
对于斜率优化的具体内容可看论文:《1D/1D动态规划优化初步》,或者其它关于斜率优化的论文。
代码如下:
#include<stdio.h> #include<iostream> #include<algorithm> #include<math.h> #include<queue> #include<stack> #include<set> #include<map> #include<vector> #include<string.h> #include<string> #include<stdlib.h> typedef long long LL; typedef unsigned long long LLU; const int nn=51000; const int inf=0x3fffffff; const LL inf64=(LL)inf*inf; const int mod=1000000007; using namespace std; int n; LL l,c[nn]; LL dp[nn]; LL sum[nn]; pair<LL,LL>que[nn]; int head,last; void add(LL x,LL y) { while(last-1>head) { if((que[last-1].second-que[last-2].second)*(x-que[last-1].first)>(que[last-1].first-que[last-2].first)*(y-que[last-1].second)) break; last--; } que[last++]=make_pair(x,y); } int main() { int i; while(scanf("%d%lld",&n,&l)!=EOF) { sum[0]=0; for(i=1;i<=n;i++) { scanf("%lld",&c[i]); sum[i]=sum[i-1]+c[i]; } dp[0]=0; head=last=0; add(0,0); LL ix; for(i=1;i<=n;i++) { ix=sum[i]+i-l-1; while(head<last-1) { if((que[head+1].first-que[head].first)>(que[head+1].second-que[head].second)*ix*2) { break; } head++; } dp[i]=ix*ix+que[head].first-2*ix*que[head].second; add(dp[i]+(sum[i]+i)*(sum[i]+i),sum[i]+i); } printf("%lld\n",dp ); } return 0; }
相关文章推荐
- 组员名单
- 块设备
- 构造函数方法创建Mat对象
- matlab画甘特图
- CentOS安装PostgreSQL数据库
- Java集合框架22课后编程题
- [转]深入分析ConcurrentHashMap
- dietpi请暂时不要升级为jessie
- SQL输出矩阵
- 编译复习第二章
- 运算符优先级口诀
- layoutSubviews总结
- POJ1062 昂贵的聘礼
- 2015-6-5(纠结的问题)
- Storyboard-故事板
- 太白---落燕纷飞第一重 Android单元测试Instrumentation和irobotium
- iOS UILabel显示html标签
- 性能优化
- LeetCode:Valid Palindrome(need update)
- Java线程池使用说明