您的位置:首页 > 其它

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]加入堆中。

【标程】

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: