NOIP2013提高组 火柴排队(重庆一中高2018级信息学竞赛测验6) 解题报告

来源:互联网 发布:车铣中心自动编程 编辑:程序博客网 时间:2024/05/16 01:40

做题思路(错解):拿到这道题时,根据排序不等式(设有两个有序数组: 及 ,则可以推出  (顺序和≥乱序和≥逆序和))可知,要使两列火柴的距离最小,则两列火柴中对应的火柴高度应处在所在序列的同一“位置”(高度在所在序列由小到大排序后位置相同)。虽然知道了这一结论,但在考试时仍不太清楚准确的解法,于是按着自己的想法,写出了错误程序。

解题思路(正解):为了在排序后能找到每个火柴原来的位置,所以用结构体来存储火柴的高度和编号。先将两列火柴分别按高度由小到大排序,此时两列火柴的距离最小,只需计算出它们由原来的位置改变到现在的位置的交换次数即可。为了方便计算,先将两列火柴排序后的编号转存到另一结构体中(使得两列火柴中每根火柴的位置对应相同),同样为了方便计算,可以将第一列火柴的编号由小到大排序(恢复至输入时的位置),则答案即为第二列火柴要恢复至输入时的位置的交换次数。计算交换次数时,最简单的算法当然是暴力枚举,但面对较大的数据规模,显然会超时,因为第二列火柴要恢复至输入时的位置(编号由小到大)的交换次数就是此时第二列火柴编号的逆序对数量,所以可以使用分治算法中的归并排序来进行优化,时间复杂度为O(n*log2n)。


#include<cstdio>#include<cstdlib>#include<iostream>#include<cstring>#include<algorithm>#include<cmath>using namespace std;const int maxn=100005;const int mo=99999997;int N;struct data1  //记录火柴{int h,id;};data1 a1[maxn],a2[maxn];struct data2  //记录两列火柴对应位置的编号{int id1,id2;};data2 b[maxn];bool cmp1(data1 aa,data1 bb){return aa.h<bb.h;}bool cmp2(data2 aa,data2 bb){return aa.id1<bb.id1;}int t[maxn];int msort(int x,int y)  //归并排序(计算逆序对数量){if(x>=y)  return 0;int m=(x+y)/2;int t1=msort(x,m);int t2=msort(m+1,y);int t3=0;int i,j,k;for(i=x,j=m+1,k=x;i<=m && j<=y;){if(b[i].id2>b[j].id2)  {t3=(t3+m-i+1)%mo;t[k++]=b[j++].id2;}else   t[k++]=b[i++].id2;}while(i<=m)  t[k++]=b[i++].id2;while(j<=y)  t[k++]=b[j++].id2;for(int i=x;i<=y;i++)b[i].id2=t[i];return (t1+t2+t3)%mo;}int main(){//freopen("match.in","r",stdin);//freopen("match.out","w",stdout);scanf("%d",&N);for(int i=1;i<=N;i++){scanf("%d",&a1[i].h);a1[i].id=i;}for(int i=1;i<=N;i++){scanf("%d",&a2[i].h);a2[i].id=i;}sort(a1+1,a1+1+N,cmp1);  //将两列火柴按高度由小到大排序sort(a2+1,a2+1+N,cmp1);for(int i=1;i<=N;i++)  //将两列火柴的编号进行转存{b[i].id1=a1[i].id;b[i].id2=a2[i].id;}sort(b+1,b+1+N,cmp2);  //按第一列火柴编号由小到大排序(恢复至输入时的顺序)int ans=msort(1,N);printf("%d\n",ans);return 0;}

考后反思:对于一道自己不太有感觉的题,要多想想学过的知识,将题目与自己熟悉的知识联系起来。

0 0
原创粉丝点击