Cut the Sequence,Sequence Partitioning,POJ3017,POJ3245,用单调队列优化的DP
2013-07-16 21:46
357 查看
为什么这两题要放到一起说呢,主要是这两题十分类似,用单调队列优化的方法是一样的,所以放在一起总结会比较印象深刻。
先说POJ3017----------Cut the Sequence
题目地址:http://poj.org/problem?id=3017
题目意思:
给你N个数和一个值M
可以将这N个数分成任意段,使得每段的和不大于M
要你使得每段的最大值加起来的和最小,求这个最小值
解题思路:
首先咱们提出一个状态转移方程:
f[i] = f[b[i]-1] + max(a[k]) b[i]<=k<=i
其中b[i]表示的是i属于的这块的头部,f[i]表示的以i结尾的块到目前为止的最大值的和
那么我们要求的和就是f
但是这个复杂度太大了,我们得想想优化方法
注意到要求某一个窗口的最大值
那么咱们就用一个优先队列来维护一个最大值队列
则,就可以得到一个可行解f[i] = f[p-1]+a[q[l]],p就是分到i的这块的块头,而a[q[l]]就是这个优先队列的队首
但是这个不一定是最优解,可能咱们这个分块不一定是最科学的,那么我们就要从前面的结果中去找最优解来和这个可行解进行比较
如果遍历的话复杂度是O(n),但是如果用BST,可以把复杂度降一降,即O(logn)
那么最优解会是什么形式呢?
对于优先对列的q[j]来说,应该是f[q[j]]+a[q[j+1]],即a[q[j]]一定比后面的大,如果不是比后面的大,那我们可以把a[q[j]]划分到后面的这个块中,反正不影响,也许还可以找到更优的解(此中有真意,要仔细的思考思考)
那么确定了之后,我们怎么保证每段的和小于等于M,可以用下面的代码:
如果还有不清楚的就看看代码:(这题不知为啥,用多case的数据处理方式就报WA,看来POJ的BUG还是很多啊)
======================分割线==================================
说完了上面的,再说下面的就容易多了
即POJ3245-------Sequence Partitioning
题目地址:http://poj.org/problem?id=3245
题目意思:
还是给你N个数对,一个限定值LIMIT
首先是把这N个数分成任意段,但是有要求
对于第p,q块,p<q
则Bp>Aq
此外,所有块的A最大值之和要小于LIMIT(是不是似曾相识?)
然后要你求出对于每块来说,B有一个和,要使所有块的和的最大值尽量小,求出这个值
首先来分块,有一些是必须要分的
对于i<j来说,如果Bi<Aj的话,那么从i~j必须和到一块去,这个是必须的,那么怎么做到这步呢
咱们可以先把B按从小到大排序,然后把A从N往前枚举,把B从排好的序往前枚举
只要B小于A,那么这个B是肯定要和A放到一起的,标记对打编号就OK,这也是A要倒着枚举的原因
具体的看代码
第一阶段的分块完成之后,我们就可以枚举这个要求的值,也就是POJ3017里面的M值
然后判断可行性就OK了,判断的代码和POJ3017很类似
所以这题相当于是两次分块
第一次是根据第一条件分了必须块,第二个就是根据枚举的M来调整,并判断可行性
下面上代码:
另外要感谢:http://blog.csdn.net/fp_hzq,我这两题都是根据他的博客学的,
详见/article/2371441.html,/article/2371447.html
先说POJ3017----------Cut the Sequence
题目地址:http://poj.org/problem?id=3017
题目意思:
给你N个数和一个值M
可以将这N个数分成任意段,使得每段的和不大于M
要你使得每段的最大值加起来的和最小,求这个最小值
解题思路:
首先咱们提出一个状态转移方程:
f[i] = f[b[i]-1] + max(a[k]) b[i]<=k<=i
其中b[i]表示的是i属于的这块的头部,f[i]表示的以i结尾的块到目前为止的最大值的和
那么我们要求的和就是f
但是这个复杂度太大了,我们得想想优化方法
注意到要求某一个窗口的最大值
那么咱们就用一个优先队列来维护一个最大值队列
则,就可以得到一个可行解f[i] = f[p-1]+a[q[l]],p就是分到i的这块的块头,而a[q[l]]就是这个优先队列的队首
但是这个不一定是最优解,可能咱们这个分块不一定是最科学的,那么我们就要从前面的结果中去找最优解来和这个可行解进行比较
如果遍历的话复杂度是O(n),但是如果用BST,可以把复杂度降一降,即O(logn)
那么最优解会是什么形式呢?
对于优先对列的q[j]来说,应该是f[q[j]]+a[q[j+1]],即a[q[j]]一定比后面的大,如果不是比后面的大,那我们可以把a[q[j]]划分到后面的这个块中,反正不影响,也许还可以找到更优的解(此中有真意,要仔细的思考思考)
那么确定了之后,我们怎么保证每段的和小于等于M,可以用下面的代码:
while(sum>m) sum-=a[p++]; if(p>i) break;这个p我们已经在之前的提到了,p就是i的这块的块头
如果还有不清楚的就看看代码:(这题不知为啥,用多case的数据处理方式就报WA,看来POJ的BUG还是很多啊)
#include<cstdio> #include<cstring> #include<set> using namespace std; #define LL long long const int maxn = 111111+10; int a[maxn],q[maxn]; LL sum,m,f[maxn],tmp; int n; multiset<int> bst; int main() { //while(~scanf("%d%I64d",&n,&m)) scanf("%d%I64d",&n,&m); { int l,r,p; l=0; r=-1; f =-1; p=1; f[0]=0; sum=0; bst.clear(); //这个是为了满足每块的和要小于m for(int i=1;i<=n;i++) { scanf("%d",&a[i]); sum+=a[i]; while(sum>m) sum-=a[p++]; if(p>i) break; //要保证队列的单调性 while(l<=r && a[i]>=a[q[r]]) { if(l<r)//删除队尾,就要删除由它相应产生的最优解 bst.erase(f[q[r-1]]+a[q[r]]); r--; } q[++r] = i; //添加一个放到集合里面去,有可能是最优解 if(l<r) bst.insert(f[q[r-1]]+a[q[r]]); //根据前面求出的p,必须相应的后移队首,因为队首已经不能在优先队列中了 while(q[l]<p) { if(l<r) bst.erase(f[q[l]]+a[q[l+1]]); l++; } f[i] = f[p-1]+a[q[l]];//由队首产生的一组可行解,不一定是最优解 tmp = *bst.begin(); if(l<r && f[i]>tmp) f[i] = tmp; } printf("%I64d\n",f ); } return 0; }
======================分割线==================================
说完了上面的,再说下面的就容易多了
即POJ3245-------Sequence Partitioning
题目地址:http://poj.org/problem?id=3245
题目意思:
还是给你N个数对,一个限定值LIMIT
首先是把这N个数分成任意段,但是有要求
对于第p,q块,p<q
则Bp>Aq
此外,所有块的A最大值之和要小于LIMIT(是不是似曾相识?)
然后要你求出对于每块来说,B有一个和,要使所有块的和的最大值尽量小,求出这个值
首先来分块,有一些是必须要分的
对于i<j来说,如果Bi<Aj的话,那么从i~j必须和到一块去,这个是必须的,那么怎么做到这步呢
咱们可以先把B按从小到大排序,然后把A从N往前枚举,把B从排好的序往前枚举
只要B小于A,那么这个B是肯定要和A放到一起的,标记对打编号就OK,这也是A要倒着枚举的原因
具体的看代码
第一阶段的分块完成之后,我们就可以枚举这个要求的值,也就是POJ3017里面的M值
然后判断可行性就OK了,判断的代码和POJ3017很类似
所以这题相当于是两次分块
第一次是根据第一条件分了必须块,第二个就是根据枚举的M来调整,并判断可行性
下面上代码:
#include<cstdio> #include<cstring> #include<set> #include<algorithm> using namespace std; const int maxn = 50000+100; int a[maxn],b[maxn],q[maxn],f[maxn],p[maxn]; int n,limit,sum,tmp; multiset<int> bst; bool cmp(int v,int u) { return b[v]<b[u]; } bool check(int m) { int l,r; l=f[0]=0; f =r=-1; int pos=1; sum=0; bst.clear(); for(int i=1;i<=n;i++) { sum+=b[i]; while(sum>m) sum-=b[pos++]; if(pos>i) return false; while(l<=r && a[i]>=a[q[r]]) { if(l<r) bst.erase(f[q[r-1]]+a[q[r]]); r--; } q[++r] = i; if(l<r) bst.insert(f[q[r-1]]+a[q[r]]); while(q[l]<pos) { if(l<r) bst.erase(f[q[l]]+a[q[l+1]]); l++; } f[i] = f[pos-1]+a[q[l]]; tmp = *bst.begin(); if(l<r && f[i]>tmp) f[i]=tmp; } return f <=limit; } int main() { scanf("%d%d",&n,&limit); int i,j; for(i=1;i<=n;i++) { scanf("%d%d",&a[i],&b[i]); q[i]=p[i]=i; } sort(p+1,p+1+n,cmp); for(j=1,i=n;i>0;i--) { while(j<=n && b[p[j]]<=a[i]) q[p[j++]]=i; } int l,r; for(i=1,j=1;i<=n;i=l,j++) { a[j]=a[i],b[j]=b[i]; for(l=i+1,r=max(q[i],i);l<=r;l++) { a[j] = max(a[j],a[l]); b[j] +=b[l]; r=max(r,q[l]); } } l=0,r=0x3f3f3f3f; int ans; n=j-1; while(l<=r) { int mid = (l+r)>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%d\n",ans); return 0; }
另外要感谢:http://blog.csdn.net/fp_hzq,我这两题都是根据他的博客学的,
详见/article/2371441.html,/article/2371447.html
相关文章推荐
- poj3017 Cut the Sequence 单调队列优化dp 好题!
- Cut the Sequence (单调队列优化DP)
- poj 3017 Cut the Sequence(dp单调队列优化)
- POJ-3017 Cut the Sequence(DP单调队列优化 + 平衡树)
- [poj3017] Cut the Sequence (DP + 单调队列优化 + 平衡树优化)
- POJ 3017 Cut the Sequence (单调队列优化DP)
- poj 3017 Cut the Sequence dp+单调队列优化
- POJ Cut the Sequence 单调队列优化DP入门题
- POJ - 3017 Cut the Sequence : 单调队列优化dp
- POJ 3017 Cut the Sequence 【DP+单调队列优化+平衡树】
- Poj 3017 Cut the Sequence (DP,单调队列优化,数据结构优化)
- POJ 3017 Cut the Sequence(DP + 单调队列优化 + 平衡树)
- poj 3017 Cut the Sequence(单调队列优化 )
- poj 3017 Cut the Sequence(DP+单调队列)
- [POJ3017] Cut the Sequence && 单调队列
- POJ_3017 Cut the Sequence 单调队列+dp+BST
- POJ - 3017 Cut the Sequence(单调队列+dp)
- POJ 3017 Cut the Sequence(dp+单调队列)
- poj 3017 Cut the Sequence(DP+单调队列+set)
- hdu 4328 cut the cake#单调队列#DP