Files
C_code/exercise/27.c
T

116 lines
3.7 KiB
C

#include <stdio.h>
#include <stdlib.h>
// 目标:在允许使用容量为 c 的栈缓冲的前提下,
// 将输入序列(商店从上到下)重排为字典序最小的最终堆叠(从下到上)。
// 操作等价于:依次读取输入;每次要么将下一个元素压入栈(若未满),要么从栈顶弹出到输出;
// 栈容量不超过 c;输出顺序即为最终堆叠的自下而上的编号。
int main(void)
{
int n, c;
if (scanf("%d %d", &n, &c) != 2)
return 0;
int *a = (int *)malloc(sizeof(int) * n);
for (int i = 0; i < n; ++i)
scanf("%d", &a[i]);
// 栈缓冲(手上带着的箱子),存储值
int *stk = (int *)malloc(sizeof(int) * n);
int len = 0; // 当前栈大小
// 单调队列(维护接下来可压入窗口中的最小值及其位置)
int *dq_val = (int *)malloc(sizeof(int) * n);
int *dq_idx = (int *)malloc(sizeof(int) * n);
int dq_head = 0, dq_tail = 0; // [head, tail) 有效
int i = 0; // 下一个待读取的输入位置
int j = 0; // 已加入单调队列的输入位置(下一个待加入的位置)
int out_cnt = 0;
while (out_cnt < n)
{
// 计算当前还能压入的最大数量 R(窗口大小)
int R = c - len;
if (R < 0)
R = 0;
// 扩充单调队列,使其覆盖 [i, min(i+R-1, n-1)]
while (j < n && j < i + R)
{
int v = a[j];
while (dq_tail > dq_head && dq_val[dq_tail - 1] > v)
--dq_tail;
dq_val[dq_tail] = v;
dq_idx[dq_tail] = j;
++dq_tail;
++j;
}
// 若栈非空,比较栈顶与窗口内最小值,决定弹出还是继续压入到最小值位置
if (len > 0)
{
int top = stk[len - 1];
int hasUpcoming = (dq_tail > dq_head);
int minUpcoming = hasUpcoming ? dq_val[dq_head] : 2147483647; // INF
if (!hasUpcoming || top <= minUpcoming)
{
// 栈顶不大于可达窗口中的最小值(或无可压入项),弹出栈顶到输出
printf("%d", top);
++out_cnt;
if (out_cnt < n)
putchar(' ');
--len;
continue;
}
}
// 否则(栈为空或栈顶较大且尚有可压入项),把队列窗口的最小值所在位置的元素压入到栈顶,然后立即弹出
if (dq_tail > dq_head)
{
int target_pos = dq_idx[dq_head];
// 逐个压入直到到达 target_pos
while (i <= target_pos)
{
stk[len++] = a[i++];
}
// 清理队列中过期项(索引 < i 的)
while (dq_tail > dq_head && dq_idx[dq_head] < i)
++dq_head;
// 弹出刚压入的最小值
int val = stk[len - 1];
printf("%d", val);
++out_cnt;
if (out_cnt < n)
putchar(' ');
--len;
}
else
{
// 没有可压入项,但仍需输出 -> 只能弹出栈顶
if (len > 0)
{
int val = stk[len - 1];
printf("%d", val);
++out_cnt;
if (out_cnt < n)
putchar(' ');
--len;
}
else
{
// 理论上不会到这里(既无可压入项又无栈元素),为安全直接中断
break;
}
}
}
putchar('\n');
free(a);
free(stk);
free(dq_val);
free(dq_idx);
return 0;
}