您的位置:首页 > 产品设计 > UI/UE

POJ 2442 Sequence 堆

2016-08-21 21:46 183 查看
    神奇的POJ......这个题原题是UVA 11997 K Smallest Sums。看过刘汝佳白书的人应该都知道吧。

    在SDUT上做题做多了,就会对复杂度有种蜜汁不信任感,而且总是错觉所有的题目,时限都是1000ms......本以为这题只有1000ms+满数据的我,绞尽脑汁想各种方法的各种优化,最后没办法了,去Google了一下,才发现这题时限居然这么长,足足6000ms,而且估计只有一小部分的数据量比较大,大多数的还是小数据,于是按我最开始的想法,直接上堆就能过......

    这里要吐槽一下传说中的百度,不说别的,就这个搜索质量,不管搜什么,只要是学术性的,前两条必定是XX培训营。然后下面搜到的东西,基本上十个结果,有六七个是重复的,两个博主一模一样的博客居然都是原创?学术上的搜索确实还是Google比较靠谱,我还特意用我的服务器架了个翻墙来Google(YouTube)。不过平时生活中的搜索,确实还是百度比较好使,搜索结果比较符合我国国情。

    还是回来看题Sequence,这个题目的题意是说,给你m个长度为n的序列,从每个序列中拿一个数,把这些数加和,一共可以得到n^m个数字(有重复),让你输出这些数字中前n小的数字。

    这题把所有的情况都存下来肯定不现实,这类找一个大的序列中前n小的数字的题目,很容易让人想到堆。因为涉及到加和的比较,因此只要再建立一个数组来存未加和时的状态,把加和之后的状态进行比较即可,这题时间给的很多,即使比较水的算法也能过(比如说我的算法),这个题我的想法大概就是用一个堆和一个辅助数组,但其实还有很大的优化空间,比如将每次输入的数字进行排序,这样就能比较早的把最小的累加和放到堆里,可以减少很多的不必要的交换,用我的未优化的算法跑了2250MS,我估计,如果优化了的话,这个思路的代码应该能跑进800ms以内(因为无用的交换确实很多)。

    下面放代码。

#include <stdio.h>
#include <string.h>

int h[2222], s[2222];

void Swap(int x, int y)//交换节点函数
{
int t = h[x];
h[x] = h[y];
h[y] = t;
}

void SiftDown(int i, int n)//向下调整 保证最大堆
{
int t;
while (i*2 <= n)//首先判断它与左儿子的大小关系
{
if (h[i] < h[i*2])//t用来记录较大值的节点编号
t = i * 2;
else
t = i;
if (i*2+1 <= n)//如果右儿子存在 则继续与右儿子比较
{
if (h[t] < h[i*2+1])//若右儿子更小 则记录右儿子的编号
t = i * 2 + 1;
}
if (t != i)//如果发现最大的节点编号不是自己 则说明子节点中有比父节点更大的
{
Swap(t, i);//交换父节点与最大的节点
i = t;//更新i的编号 继续向下调整子节点
}
else
return;//此时父节点比两个子节点都要大 不需要继续调整 跳出调整函数
}
}

void Creat(int n)//将数组建立成堆
{
for (int i = n / 2; i >= 1; i--)//从最后一个非叶节点依次向上调整
SiftDown(i, n);
}

void HeapSort(int n)//堆排序函数 将此时的储存堆的数组排序成有序的数组
{
while (n > 1)
{
Swap(1, n);//每次交换堆顶(最大值)与此时的数组尾
n--;//数组长度减一
SiftDown(1, n);//继续调整堆 使最大值在堆顶
}//数组长度为1时 数组内所有元素由小到大有序
}

int main()
{
int T, m, n, i, j, k, c;
scanf("%d", &T);
while (T--)
{
scanf("%d%d", &m, &n);
for (i = 1; i <= n; i++)
scanf("%d", &h[i]);
Creat(n);
for (i = 1; i < m; i++)
{
for (j = 1; j <= n; j++)
s[j] = h[j];//将堆中的元素复制到辅助数组中
scanf("%d", &c);
for (j = 1; j <= n; j++)
h[j] += c;//先使堆中的每个元素为上一状态时的大小加上下一行的第一个数字
for (j = 2; j <= n; j++)
{
scanf("%d", &c);
for (k = 1; k <= n; k++)//依次遍历每种加和情况 调整堆中序列
{
if (c + s[k] < h[1])//每次与堆中最大的元素相比较 若小于此元素 则替换这个数字 并调整堆
{
h[1] = c + s[k];
SiftDown(1, n);
}
}
}
}
HeapSort(n);//将堆中的元素排序
for (i = 1; i <= n; i++)//输出排好序的堆中元素
printf(i==n?"%d\n":"%d ", h[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: