目录
题面
Description
描述
逆序数可以用来是描述一个序列混乱成度的量。例如,“DAABEC”的逆序数为5,其中D大于它右边的4个数,E大于它右边的1个数,4+1=5;又例如“ZWQM”的逆序数为3+2+1+0=6。
现在有许多长度一样的字符串,每个字符串里面只会出现四种字母(A,T,G,C)。你现在被要求编写程序将这些字符串按照他们的逆序数进行排序。
Input
输入格式
第一行包括两个正整数,第一个正整数N给出了字符串的长度,第二个正整数M给出了字符串的数量。(1<=N,M<=100)
接下来M行每行一个长度为N的字符串,只包含A,T,G,C四种字母。
Output
输出格式
将输入的字符串按照其逆序数进行排序,如果两个字符串的逆序数相等,则按照输入中两者的先后顺序进行排列。
Sample Input
样例输入
10 6 AACATGAAGG TTTTGGCCAA TTTGGCCAAA GATCAGATTT CCCGGGGGGA ATCGATGCAT
Sample Output
样例输出
CCCGGGGGGA AACATGAAGG GATCAGATTT ATCGATGCAT TTTTGGCCAA TTTGGCCAAA
Hint
选择排序是不稳定的
题目分析
题目中说,要我们计算每个字符串的逆序数,并按逆序数升序输出。
那么现在基本可以确定是个结构体排序题了。
对于每个给出的DNA序列,我们有多种成员属性来描述它,并且要根据成员属性的值来进行排序。
因此可以先写出结构体代码
struct ob
{
char seq[105];
int nixv;//逆序数
};
逆序数的定义也很简洁,两层循环搞定
for(int i = 0; i < len ; i++)
{
for(int j = i; j < len ; j++)
{
if(dna[x].seq[i] > dna[x].seq[j])
{
dna[x].nixv++;
}
}
}
题中给出的数据范围并不大,因此这题用什么排序都能保证不会TLE和MLE,但是重要的是题意中的一点以及提示
如果两个字符串的逆序数相等,则按照输入中两者的先后顺序进行排列。
选择排序是不稳定的
第一眼看过去云里雾里的……
要知道,排序算法有稳定与不稳定两种,而不稳定排序有可能导致相等元素出现顺序与排序后顺序不同。
对于几个相等的数字来说,顺序是无法分辨的,但是对于拥有多重属性的结构体来说,问题就大了。
因为不同的DNA序列任然有可能具有相等的逆序数,如果你使用 C++ 的 <algorithm> 库中自带的 sort() 函数(本质上是改良了的快速排序,仍然是不稳定的),贸然用一个关键词进行排序并不是明智的选择,这可能导致先出现的序列A在排序后出现在和它具有相同逆序数的序列B后面(无情wa掉hhh)。
至此,这道题的全部已经被我们挖掘出来,其实是双关键字结构体排序,一个是逆序数(代码中 nixv),另一个是出现顺序(代码中 sn)。
struct ob
{
char seq[105];
int sn;
int nixv;
}
解题方法
方法一
既然是双关键字结构体排序,那就写一个对应的 cmp 函数
bool cmp(ob x, ob y)
{
if(x.nixv == y.nixv)//注意:排序的优先级还是逆序数更高
{
return x.sn < y.sn;
}else
{
return x.nixv < y.nixv;
}
}
用这个排序规则来使用C++的<algorithm>库中自带的 sort 函数,就不会有任何问题
AC代码一
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
struct ob
{
char seq[105];
int sn;
int nixv;
}dna[105];
ob dna2[105];
bool cmp(ob x, ob y)
{
if(x.nixv == y.nixv)
{
return x.sn < y.sn;
}else
{
return x.nixv < y.nixv;
}
}
int len,cnt;
int main()
{
scanf("%d%d", &len, &cnt);
getchar();
for(int i = 0; i < cnt ; i++)
{
cin >> dna[i].seq;
dna[i].sn = i;
for(int j = 0; j < len ; j++)
{
for(int k = j; k < len ; k++)
{
if(dna[i].seq[j] > dna[i].seq[k])
{
dna[i].nixv += 1;
}
}
}
}
sort(dna, dna + cnt, cmp);
for(int i = 0; i < cnt ; i++)
{
cout << dna[i].seq << endl;
}
return 0;
}
方法二
前面说过,不稳定的排序算法可能导致相等元素出现顺序与排序后顺序不同,那么,用稳定的排序算法就可以避免了,比如基数排序,归并排序等等。
C++本身有自带的稳定排序函数 stable_sort() ,我这里则选择手写的归并排序模板
AC代码二
#include <cstdio>
#include <iostream>
using namespace std;
struct ob
{
char seq[105];
int nixv;
}dna[105];
ob dna2[105];
void sort(ob a[],ob b[], int start, int end)
{
if (start >= end)
return;
int len = end - start, mid = (len / 2) + start;
int start1 = start, end1 = mid;
int start2 = mid + 1, end2 = end;
sort(a, b, start1, end1);
sort(a, b, start2, end2);
int pointer = start;
while (start1 <= end1 && start2 <= end2)
{
b[pointer++] = a[start1].nixv <= a[start2].nixv ? a[start1++] : a[start2++];
}
while (start1 <= end1)
{
b[pointer++] = a[start1++];
}
while (start2 <= end2)
{
b[pointer++] = a[start2++];
}
for (pointer = start; pointer <= end; pointer++)
{
a[pointer] = b[pointer];
}
}
int len,cnt;
int main()
{
scanf("%d%d", &len, &cnt);
getchar();
for(int i = 0; i < cnt ; i++)
{
cin >> dna[i].seq;
for(int j = 0; j < len ; j++)
{
for(int k = j; k < len ; k++)
{
if(dna[i].seq[j] > dna[i].seq[k])
{
dna[i].nixv += 1;
}
}
}
}
sort(dna, dna2, 0, cnt - 1);
for(int i = 0; i < cnt ; i++)
{
cout << dna[i].seq << endl;
}
return 0;
}
总结
不想wa就好好看题!
PS:归并排序此处三目运算符 ’?‘ 前面的 '<=' 替换成 '<',并不会影响排序结果,但是会让归并的排序结果具有不稳定排序算法的性质,即相对位置改变。我因为这个wa了很多次www
while (start1 <= end1 && start2 <= end2)
{
b[pointer++] = a[start1].nixv <= a[start2].nixv ? a[start1++] : a[start2++];
}
题外话
本人是山师ACM队小菜鸡一枚,第一次发博客,如有不足还请在评论中指出。
☆*: .。. o(≧▽≦)o .。.:*☆