ssoj1020编译优化(双向链表+堆优化)
2015-10-30 20:50
288 查看
【问题描述】
众所周知,衡量一个编译器是否优秀的标准,除了它的编译速度和正确性以外,编译出的代码的质量也很重要。最近,作为XCC系列编译器作者的Dr. X发明了一种跨时代的优化算法:“NanGe不等式优化”。一个程序可以看成是由若干个连续的函数构成的,NanGe不等式算法能针对某一个函数进行优化,得到一个优化效果值, 不同的函数的效果值可能是不同的。但这个算法还有一个很大的Bug:
该算法不能同时优化相邻的两个函数,否则就会直接Compile Error,值得注意的是,一个程序的第一个函数和最后一个函数也算是相邻的。
现在给你一个程序从头到尾每个函数的优化效果值,Dr. X想用NanGe不等式对该程序的M个函数进行优化,他该怎么选择才能使总的优化效果值最大(前提是不能出现错误)?如果错误不能避免,请输出“Error!”
【精炼任务】:给出由n个数组成的环,取某个数就可以得到它的分数,相邻的两个数不能同时取。问取m个数可以得到的最大分数。
对于前8个数据,我们采用搜索方法可以解决,但搜索的效率直接决定得分。我想到的优化有两点:1排序、2剪枝。(很简单,不详细说,详见程序)
特别的,如果这个环上的点是偶数个,我们可以把此题转化为带权匹配。在环上两个数之间建点,点恰好有n个,可以黑白染色构成二分图。而把数当做边。在这个图做带权匹配就是最后结果了。(由于匹配中同一个点引出的两条边是不可能同时取到的,这正好符合了相邻两个数不能同时取的性质)
符合这个算法的数据有:1、3、5、7、9、10、11(O(n^4))。如果带权匹配写的好(O(n^3)外加系数小),可以再过13、15、17三个点。
我们再考虑这个题的简单版:在一个长度为n的数列中,选m个数,两个相邻的数不能同时选,要求取数的和最大。(即把原题的环改为链)
如果我们选取的数的集合叫C。C一开始是空集,每一次取数操作就会让C集合中多一个数,直到够m个数为止。而每次取数时,如果要取的这个数x左边右边都没被取,那这个数就可以直接取走,x就是取这个数带来的价值;如果这个要取的数x的左边的数y(右边完全一样,这里略)已经被取走了,那y必定要放回去,而把y左边的数z取出来以保证本次操作能让C集合多一个数,若z的左边的数w也被取了,那我们再用w左边的数代替,总之我们进行了一个类似01翻转的操作,使C集合多了一个数。而我们取这个数使C集合的总和增加的量,就是取这个数能带来的价值。
我们可以证明:每次操作带来的价值,一定是单调递减的(挺显然的)。所以我们可以用贪心的方法,取够m个为止,并且保证算法是正确的。
接下来是考虑时间复杂度,暴力做是O(N^2)。我们也可以用线段树(或者堆)来维护这一操作,从而达到O(NLogN)的时间复杂度。
回到原问题环。直接用这个算法是行不通的,因为在环上,左边进行的01翻转和右边进行的01翻转可能会相遇!最后就会导致两个1相邻。
我们可以想到一个略暴力的方法:枚举每一个数,不要它!把这个数剔除后所形成的链,通过刚才的算法求出不取这个数的情况下的最优值。最后一定可以得到最优值。这样时间复杂度为O(N^2*LogN)。
在这个算法的基础上,稍作优化,就可以得到一个很好的算法:我们考虑两个相邻的数a和b,分三种情况:a和b都取(题目不允许)、不取a、不取b(虽然后两者有重叠,但包含了所有情况)。也就是说,我们并不需要枚举每一个数不要它,只需要针对两个相邻的数a和b,比较不要a的最优值更好还是不要b的最优值更好就可以了。最终复杂度为O(NLogN)。
【题目考点】贪心+双向链表+堆优化
【方法1】对于前4个数据,我们采用搜索方法可以解决,但搜索的效率直接决定得分。
【方法2】动态规划,设f[i][j][0]表示前i个位置选择j个种树且第i个位置没有种树,f[i][j][1]表示表示前i个位置选择j个种树且第i个位置必须种树,则转化为非常简单的O(n^2)动规,转台转移方程为:
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
f[i][j][1]=f[i][j-1][0];
【方法3】这个题标准解法是借鉴网络流中的残余流思想,用堆来维护解决。映射建大根堆,记录每一个数值在堆中的位置好方便删除操作。每回出堆顶元素后,a[k]=a[l[k]]+a[r[k]]-a[k],l[k]和r[k]是k的左边节点和右边节点,即双链表思想,再将a[l[k]]和a[r[k]]删除,将新的a[k]加入堆中。
【标程】
【代码】
众所周知,衡量一个编译器是否优秀的标准,除了它的编译速度和正确性以外,编译出的代码的质量也很重要。最近,作为XCC系列编译器作者的Dr. X发明了一种跨时代的优化算法:“NanGe不等式优化”。一个程序可以看成是由若干个连续的函数构成的,NanGe不等式算法能针对某一个函数进行优化,得到一个优化效果值, 不同的函数的效果值可能是不同的。但这个算法还有一个很大的Bug:
该算法不能同时优化相邻的两个函数,否则就会直接Compile Error,值得注意的是,一个程序的第一个函数和最后一个函数也算是相邻的。
现在给你一个程序从头到尾每个函数的优化效果值,Dr. X想用NanGe不等式对该程序的M个函数进行优化,他该怎么选择才能使总的优化效果值最大(前提是不能出现错误)?如果错误不能避免,请输出“Error!”
【精炼任务】:给出由n个数组成的环,取某个数就可以得到它的分数,相邻的两个数不能同时取。问取m个数可以得到的最大分数。
对于前8个数据,我们采用搜索方法可以解决,但搜索的效率直接决定得分。我想到的优化有两点:1排序、2剪枝。(很简单,不详细说,详见程序)
特别的,如果这个环上的点是偶数个,我们可以把此题转化为带权匹配。在环上两个数之间建点,点恰好有n个,可以黑白染色构成二分图。而把数当做边。在这个图做带权匹配就是最后结果了。(由于匹配中同一个点引出的两条边是不可能同时取到的,这正好符合了相邻两个数不能同时取的性质)
符合这个算法的数据有:1、3、5、7、9、10、11(O(n^4))。如果带权匹配写的好(O(n^3)外加系数小),可以再过13、15、17三个点。
我们再考虑这个题的简单版:在一个长度为n的数列中,选m个数,两个相邻的数不能同时选,要求取数的和最大。(即把原题的环改为链)
如果我们选取的数的集合叫C。C一开始是空集,每一次取数操作就会让C集合中多一个数,直到够m个数为止。而每次取数时,如果要取的这个数x左边右边都没被取,那这个数就可以直接取走,x就是取这个数带来的价值;如果这个要取的数x的左边的数y(右边完全一样,这里略)已经被取走了,那y必定要放回去,而把y左边的数z取出来以保证本次操作能让C集合多一个数,若z的左边的数w也被取了,那我们再用w左边的数代替,总之我们进行了一个类似01翻转的操作,使C集合多了一个数。而我们取这个数使C集合的总和增加的量,就是取这个数能带来的价值。
我们可以证明:每次操作带来的价值,一定是单调递减的(挺显然的)。所以我们可以用贪心的方法,取够m个为止,并且保证算法是正确的。
接下来是考虑时间复杂度,暴力做是O(N^2)。我们也可以用线段树(或者堆)来维护这一操作,从而达到O(NLogN)的时间复杂度。
回到原问题环。直接用这个算法是行不通的,因为在环上,左边进行的01翻转和右边进行的01翻转可能会相遇!最后就会导致两个1相邻。
我们可以想到一个略暴力的方法:枚举每一个数,不要它!把这个数剔除后所形成的链,通过刚才的算法求出不取这个数的情况下的最优值。最后一定可以得到最优值。这样时间复杂度为O(N^2*LogN)。
在这个算法的基础上,稍作优化,就可以得到一个很好的算法:我们考虑两个相邻的数a和b,分三种情况:a和b都取(题目不允许)、不取a、不取b(虽然后两者有重叠,但包含了所有情况)。也就是说,我们并不需要枚举每一个数不要它,只需要针对两个相邻的数a和b,比较不要a的最优值更好还是不要b的最优值更好就可以了。最终复杂度为O(NLogN)。
【题目考点】贪心+双向链表+堆优化
【方法1】对于前4个数据,我们采用搜索方法可以解决,但搜索的效率直接决定得分。
【方法2】动态规划,设f[i][j][0]表示前i个位置选择j个种树且第i个位置没有种树,f[i][j][1]表示表示前i个位置选择j个种树且第i个位置必须种树,则转化为非常简单的O(n^2)动规,转台转移方程为:
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
f[i][j][1]=f[i][j-1][0];
【方法3】这个题标准解法是借鉴网络流中的残余流思想,用堆来维护解决。映射建大根堆,记录每一个数值在堆中的位置好方便删除操作。每回出堆顶元素后,a[k]=a[l[k]]+a[r[k]]-a[k],l[k]和r[k]是k的左边节点和右边节点,即双链表思想,再将a[l[k]]和a[r[k]]删除,将新的a[k]加入堆中。
【标程】
#include<iostream> using namespace std; int n,m; int L[200001],R[200001]; int d[200001],pos[200001],a[200001]; void up(int x) { int i=x; while(i>1&&a[d[i]]>a[d[i/2]]) { swap(d[i],d[i/2]); swap(pos[d[i]],pos[d[i/2]]); i/=2; } } void down(int x) { int i=x,j; while(i*2<=n) { if(i*2==n||a[d[i*2]]>a[d[i*2+1]])j=i*2; else j=i*2+1; if(a[d[i]]>a[d[j]])return; swap(d[i],d[j]); swap(pos[d[i]],pos[d[j]]); i=j; } } int main() { int i,j; cin>>n>>m; if(n/2<m){cout<<"Error!";return 0;} for(i=1;i<=n;i++) { cin>>a[i]; d[i]=i;pos[i]=i;up(i); L[i]=i-1;R[i]=i+1; } L[1]=n;R =1; int ans=0; while(m--) { int x=d[1]; ans+=a[x]; a[x]=a[L[x]]+a[R[x]]-a[x]; a[L[x]]=-1111;down(pos[L[x]]); a[R[x]]=-1111;down(pos[R[x]]); down(1); L[x]=L[L[x]]; R[x]=R[R[x]]; R[L[x]]=x; L[R[x]]=x; } cout<<ans; //system("pause"); return 0; }
【代码】
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <queue> using namespace std; const int maxn=200005; struct data{ int num,pos; bool operator<(const data&b)const{return(num<b.num);} }a[maxn]; priority_queue<data>q; int n,m,ans=0,l[maxn],r[maxn]; bool vst[maxn]; inline int get(){ char c;bool f=0; while(!isdigit(c=getchar()))if(c=='-')f=1; int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48; if(f)v=-v; return v; } int main(){ n=get();m=get(); for(int i=1;i<=n;++i)a[i].num=get(),a[i].pos=i,q.push(a[i]),l[i]=i-1,r[i]=i+1; l[1]=n;r =1; if(n/2<m){printf("Error!\n");return 0;} for(int i=1;i<=m;++i){ while(!q.empty() && vst[q.top().pos])q.pop(); data x=q.top();q.pop();int t=x.pos; ans+=x.num; x.num=a[l[t]].num+a[r[t]].num-x.num; a[t].num=x.num; q.push(x); vst[l[t]]=1;vst[r[t]]=1; l[t]=l[l[t]];r[t]=r[r[t]];l[r[t]]=t;r[l[t]]=t; } printf("%d\n",ans); return 0; }
相关文章推荐
- 细说.NET中的多线程 (四 使用锁进行同步)
- AIR文件操作:使用文件对象操作文件和目录 .
- 从尾到头打印链表
- 失业潮来了,未来10年靠什么赚钱?
- 树的遍历
- Java基础学习13(JDK,Java UML)
- 项目管理 版本号管理之语义化版本
- css元素边框发光效果——box-shadow
- 替换空格
- sicily 1090. Highways
- BeautifulSoup的使用学习笔记
- Xcode 7.1项目中Objective-C和swift混合编程(一)
- hdu 1254 推箱子(嵌套搜索)
- ios响应者链
- Windows 远程连接Hbase常见错误
- Leetcode-Remove Duplicates from Sorted List
- BZOJ4300 绝世好题
- 一个有序正整数集S,若要保证整数集中所有的数通过组合(相加)可以表示1~N中任意一个正整数
- C++编写安全OCX,IE不弹出安全提示
- 腾讯新闻评论数据爬取