您的位置:首页 > 编程语言 > C语言/C++

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

2016-08-01 17:05 459 查看


做题思路(错解):拿到这道题时,根据排序不等式(设有两个有序数组:


 及 

,则可以推出

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

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


考后反思:对于一道自己不太有感觉的题,要多想想学过的知识,将题目与自己熟悉的知识联系起来。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ NOIP 2013
相关文章推荐