这周的一道程序设计题兼sicily1443 Printer Queue题解

题目懒得贴了,直接给链接吧

http://soj.me/1443

一看数据规模,很容易想到模拟解决,用一个队列来保存数据,每次遍历一次看队首优先级是否为最高的,若是则pop掉,不是则push到队尾,是否轮到目标打印可以通过另外一个变量记录或者把队列元素弄成结构体。最坏情况是优先级为1 2 3 ... n的情况,大概是O(n³)

不难看出“判断优先级是否为最高”这部分明显可以优化,加一个数组记录每种优先级的数量即可,优化后时间复杂度为O(n²)

然后我又想出了一种不使用模拟的算法,如下:

因为没被处理的元素会被push到队尾,所以可以把队列视为一个环,然后可以得出以下结论
①某一堆处于不是最高级的等级的元素的打印开始于刚好比它大的元素的最后一个被打印后

比如有2...3...2,那么这些2都在3及以上的元素打印后才开始被打印,准确地说,是最后一个3(比2大的元素中最小的)被打印后
②优先级最高的元素中被打印的最后一个就是原顺序的最后一个

这很显然,就不细说了

由这两点就可以递推出刚好比“我”优先的那一级的最后一个打印的元素


 递推过程:

①找到优先级最大的那一级中最后一个元素(到这个地方会开始打印比它低级的元素)

②不断往前找到刚好比当前的数优先级低的那级的最后一个元素,直到当前的数刚好比“我”的优先级高

③继续往后遍历直到“我”被打印,统计这个过程中与“我”同级的元素

为了使②不需要每次都遍历和③中统计方便,需要在输入时维护好每个优先级的总数、每个元素以前刚好比它优先级低的第一个元素的位置、每个优先级最后一个元素的位置


算法演示(没有这一步恐怕未来的我也看不懂= =):

原序列

4 2 3 1 2 2 3 4 1

目标为4号位(从0开始编号)的2


执行①,此处最高级是4,易知最后一个4在7号位

4 2 3 1 2 2 3 4 1


那么此时会从这个4开始打印3,往前找就可以知道最后一个被打印的3是在6号位

4 2 3 1 2 2 3 4 1


此时会从这个3开始打印2,由于2就是我们的目标的优先级,所以我们从这个3开始往后遍历,直到打印目标为止

被访问的元素             优先级为2的被打印的数量

4 2 3 1 2 2 3 4 1                  0

4 2 3 1 2 2 3 4 1                  0

4 2 3 1 2 2 3 4 1                  0

4 2 3 1 2 2 3 4 1                  1

4 2 3 1 2 2 3 4 1                  0

4 2 3 1 2 2 3 4 1                  0

4 2 3 1 2 2 3 4 1                  2

此时目标已被打印,所以后面的2不统计

结果为2(4的数量) + 2(3的数量) + 2(被打印的2的数量) = 6

这只是为了说明原理,跟最终的实现不一样……



看完上面的,应该可以看懂我给出的代码,改了挺久才写对。时间复杂度为O(kn),k为优先级的数量,大头在维护last_smaller_pos数列的时候,要找到刚好比这个数优先级低的那个优先级,这里可以用stl set(应该是map才对,写错了)优化为log2(k),不过当时太累懒得写了

#include 

int main() {
    int t, n, m, res, i, j, p;
    int last_pos[10], n_priority[10];  // last_pos是对应优先级的最后一个
    int num[101], last_smaller_pos[101];
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &n, &m);
        for (i = 1; i <= 9; i++) {
            last_pos[i] = -1;
            n_priority[i] = 0;
        }

        for (i = 0; i < n; i++) {
            scanf("%d", &num[i]);
            last_pos[num[i]] = i;
            ++n_priority[num[i]];
        }

        /*此时last_pos已经好了,
         *再遍历一次维护好比m高级的last_smaller_pos*/
        for (i = 0; i < n; i++) {
            if (num[i] > num[m]) {
                j = num[i] - 1;
                while (last_pos[j] == -1)
                    j--;
                last_smaller_pos[i] = last_pos[j];
            }
            last_pos[num[i]] = i;
        }

        /*如果是最高级,直接从头开始扫同级的,
         *否则递推确定刚好比它大的那一级的最后一个完成的人的位置*/
        i = 9;
        while (last_pos[i] == -1 && i > num[m])
            --i;
        if (i == num[m]) {  // m是最高级
            p = -1;
        } else {
            p = last_pos[i];
            while (num[last_smaller_pos[p]] > num[m]) {
                p = last_smaller_pos[p];
            }
        }

        res = 0;
        for (i = num[m] + 1; i <= 9; ++i)
            res += n_priority[i];
        do {
            p = (p + 1) % n;
            if (num[p] == num[m])
                ++res;
        } while (p != m);

        printf("%d\n", res);
    }
    return 0;
}

你可能感兴趣的:(C/C++,算法,sicily)