时间限制: 1 s
空间限制: 128000 KB
题目等级 : 青铜 Bronze
题目链接
题目描述 Description
刚进我们的新学校~首先感受到了校园的样子与小学完全不同~来到了我们的机房,我也看到了几个同学~有的认识,有的不认识!(MHZ:废话! LCZ:现在流行说废话凑字数。 JHT:汗~)
现在,为了让大家更熟悉,更容易交流,于是老师决定排座位!现在,老师通过同学们的自我介绍明白了同学们互相的熟悉度,老师安排好座位后,告诉了同学们一个惊天的好消息(MHZ:哇,快说!):我们可以去参观广播操的排练现场!
来到了现场,我们通过仔细的观察,又发现了一个秘密,我们几个过去参观的同学被老师盯上了!老师发现他们的同学做广播的动作极不整齐,让我们告诉他们应该做那个动作会用的时间最少而最整齐。
MHZ由于是第一个到现场的,所以,这个任务就给他了~作为他的好朋友,你要帮帮他哦!
有n个班的老师求助~我们都要帮他们(JHT:要不要给他们每人发一瓶水 MHZ:我也要,其他没意见 JHT:那还是算了~)。
每个班里都有相对应的m个学生,由于老师初期教的不好,于是动作五花八门的!居然最多有50种动作!!!现在知道越是接近的两个动作,他们序号就越接近。而要改掉这个动作的时间就是你要改的动作的序号减去现在动作序号的绝对值~老师问你一共需要多少时间才能纠正全班的错误(LJX:提醒一下,老师只有一个,要一个一个同学地纠正~)。
输入描述 Input Description
第1行:一个整数n
第2~n+1行:首先是一个整数m,然后是m个人的动作,都用0~49的整数组成。
输出描述 Output Description
n行,每行两个整数:第一个整数表示选的动作的序号,第二个整数表示纠正动作要花的时间(LCZ:时间一样的话按序号小的输出~老师认为序号的动作越小越美观~)。
样例输入 Sample Input
3
4 0 1 2 3
5 0 0 0 0 0
4 0 0 1 1
样例输出 Sample Output
1 4
0 0
0 2
数据范围及提示 Data Size & Hint
100%的数据:n≤40,m≤50000
解题思路
首先必须吐槽一下,这题目实在是太唠叨了。
因为问题最大规模为50000,简单粗暴的搜索肯定是不可能的,最开始想到的是排序后找中位的方法。但是问题规模在50000左右,而排序交换元素的操作是非常耗费时间的。
注意到动作只有50种(0~49),那么如果利用装桶的方法,即统计出每个动作的人数再暴力搜索,这样规模就下滑到了50,也许会比排序后用中位数的算法更快,就试了一下,似乎确实如此,而且需要的空间更少。
而且不论规模增大或者缩小,所需时间都是恒定的,近似一个常数时间。
实测这种方法平均耗时82.2ms,而排序后找中位平均耗时107.2ms 。
另外用C++的,输入输出最好也用scanf/printf
,cin/cout
在这种情况效率实在太低了(在该问题中实测后者是前者的两倍)。
AC代码
int main(void)
{
int action[50] = { 0 };
int n, m, tmp, res, cost, minimumCost;
scanf("%d", &n);
while (n--)
{
scanf("%d", &m);
while (m--)
{
scanf("%d", &tmp);
action[tmp]++; //action用于标记每个动作的人数
}
minimumCost = 2000000000; //核心是暴力搜索,所以需要一个变量记录当前最小代价
//不管人数多少,该算法需要的时间是恒定的。
for (int i = 0; i < 50; i++)
{
cost = 0;
for (int front = 0; front < i; front++)
cost += action[front] * (i - front);
for (int rear = i + 1; rear < 50; rear++)
cost += action[rear] * (rear - i);
if (cost < minimumCost)
{
minimumCost = cost;
res = i;
}
}
printf("%d %d\n", res, minimumCost);
for (int i = 0; i < 50; i++) action[i] = 0; //reset Array
}
return 0;
}