<数据结构>XDOJ334.分组统计
问题与解答
问题描述
先输入一组数,然后输入其分组,按照分组统计出现次数并输出,参见样例。
输入格式
输入第一行表示样例数m,对于每个样例,第一行为数的个数n,接下来两行分别有n个数,第一行有n个数,第二行的n个数分别对应上一行每个数的分组,n不超过100。数和分组号的值都不超过10000。
输出格式
按顺序输出各个样例的结果。输出格式参见样例,按组号从小到大输出,组内数字也按编号从小到大输出。
样例输入
1
7
3 2 3 8 8 2 3
1 2 3 2 1 3 1
样例输出
1={2=0,3=2,8=1}
2={2=1,3=0,8=1}
3=
//分组统计
//sort排序结构体数组,hash表缩短查找时间
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
#define MaxN 10010
struct Node{
int Data;
int Group;
};
Node Elements[100]; //结构体数组,存储元素及其对应的组号
int Count[MaxN]; //hash表:1.记录所有出现的元素;2.记录每组各元素出现的次数
vector<int> All_Data; //将hash中的元素转移到变长数组All_Data中避免多次遍历Count数组
void Divide(int n,int Max_Group); //核心函数:划分组别并输出
bool cmp(Node x, Node y);
/*对Elements进行排序时的比较函数:
1.先由组别从小到大;2.组别相同时按元素大小从小到大
*/
void Print_Result(int group_num); //输出结果
vector<int> AllGroupNumber; //存储所有组的编号(组号不一定连续,如1、2、4)
int AllGroup[100]; //将AllGroupNumber复制到AllGroup好调用Sort排序
void Copy(); //复制函数
bool Find(int group); //寻找AllGroupNumber中是否已经有组号group
int main(){
int m,n,i,data,group,Max_Group;
scanf("%d", &m);
while(m--){
fill(Count, Count+MaxN, -1); //Count数组初始化为-1
scanf("%d", &n);
for(i = 0; i < n; i++){ //输入数据
scanf("%d", &data);
Elements[i].Data = data;
if(Count[data] == -1)
Count[data]++;
}
for(i = 0; i < n; i++){ //输入组别
scanf("%d", &group);
Elements[i].Group = group;
if(!Find(group))
AllGroupNumber.push_back(group); //AllGroupNumber记录所有输入的组号
}
for(i = 0; i < MaxN; i++){ //将所有输入元素由小到大输入All_Data
if(Count[i] != -1)
All_Data.push_back(i); //All_Data记录所有输入的元素
}
Divide(n,Max_Group); //调用核心函数
All_Data.clear(); //清空All_Data
AllGroupNumber.clear(); //清空AllGroupNumber
}
}
void Divide(int n,int Max_Group){
int group_num,data,index,i;
sort(Elements, Elements+n, cmp); //对Elements数组进行sort排序,用自定的cmp函数
int size = AllGroupNumber.size();
Copy(); //把AllGroupNumber的数据复制到AllGroup中(无法对vector调用sort)
sort(AllGroup, AllGroup+size); //对AllGroup进行sort排序,默认从小到大
for(index = 0; index < size; index++){ //循环处理每一个组
group_num = AllGroup[index]; //得到组号[已经从小到大有序]
fill(Count, Count+MaxN, 0); //重新初始化Count数组,为0
for(i = 0; i < n; i++){ //统计Elements数组中第group组的各个元素的出现次数
if(Elements[i].Group != group_num)
continue;
data = Elements[i].Data;
Count[data] += 1; //Count[data] = number表示元素data的出现次数是number
}
Print_Result(group_num); //打印结果
}
}
void Print_Result(int group_num){
int i,count;
printf("%d={", group_num);
for(i = 0; i < All_Data.size(); i++){ //对于每一个输入的元素,打印出现次数
count = Count[All_Data[i]];
if(i == All_Data.size()-1)
printf("%d=%d}\n", All_Data[i], count);
else
printf("%d=%d,", All_Data[i],count);
}
}
bool cmp(Node x, Node y){ //Elements的比较函数
if(x.Group != y.Group) //先有组别从小到大
return x.Group <= y.Group;
else //组别相同时,按元素大小从小到大
return x.Data <= y.Data;
}
bool Find(int group){ //判断AllGroupNumber中是否已经含有group
int i,flag = 0;
for(i = 0; i < AllGroupNumber.size(); i++){
if(AllGroupNumber[i] == group)
flag = 1;
}
return flag;
}
void Copy(){ //将AllGroupNumber中的元素复制到AllGroup
int i,size;
size = AllGroupNumber.size();
for(i = 0; i < size; i++)
AllGroup[i] = AllGroupNumber[i];
}
题后反思:核心算法简单,细节处理复杂
要解决的问题
- 要得到所有的组号,并由小到大排序。【因为组号不一定连续如1、3、5、8组,所以每个值都要记录】
- 要得到所有的输入元素,并由小到大排序。【分组统计时,所有输入元素在该组出现的次数都要输出】
- 要统计所有输入元素在每个组中的出现次数。
对应的解决方法
- 用AllGroupNumber记录无重复地记录所有输入的组别。在对AllGroupNumber进行排序。【由于无法直接对vector排序,所以不得不重新引入AllGroup数组并将AllGroupNumber的元素复制进去】
- 参考Dijstra算法的Vis数组的思想【本质就是hash】,创建Count数组,初始化为-1。在输入元素data时,将
Vis[data]+1
。输入结束后遍历Vis数组,Vis[i]!=0
说明i
是输入的元素,把它添加到变长数组vector<int> All_Data
中,这样需要再次遍历所有出现的元素时,就不必遍历Vis[MaxN]
数组。由于i是从小到大遍历Vis数组,所以All_Data
中的元素已经从小到大有序。 - 遍历
Elements
数组,统计所有输入元素在每个组中的出现次数。以第一组为例。- 当把所有组号为1(
Elements[data].Group == 1
)的元素的出现次数+1(Count[Elements[data].Data]+1
)。 - 最终遍历按照
vector<int> All_Data
中元素的出现顺序遍历Count
数组就得到了各个元素在该组的出现结果 - 其他组则重置
Count
数组为0,重复上述步骤
- 当把所有组号为1(
细节实现繁琐
- AllGroupNumber无法直接Sort排序,因此设置AllGroup数组进行复制,再对AllNumber排序
- vector类型没有
.find()
方法,所以不得不手动实现AllGourpNumber
的Find()
- 原以为用Sort函数+hash查找来做会比较简单,没想到各种细节的添加导致了程序非常臃肿,思路不是非常清晰简洁。