原题链接:http://acm.fzu.edu.cn/problem.php?pid=2260
Problem 2260 Card Game
有如下取牌游戏:
1. 桌面上有n张卡牌从左到右排成一行,每张卡牌上有一个数字;
2. 游戏按轮次进行,每一轮中取掉所有比左边数值小的卡牌;
3. 当无牌可取的时候则游戏结束。
比如初始卡牌为{5, 6, 3, 7, 4, 1, 2},共需2轮取牌。取牌过程如下(小括号为每轮取掉的牌):
{5, 6, 3, 7, 4, 1, 2}
==> {5, 6, (3), 7, (4), (1), 2}
==> {5, 6, 7, 2}
==> {5, 6, 7, (2)}
==> {5, 6, 7}
现按顺序给定初始的卡牌数字,请求出游戏结束时取牌的总轮次,并输出结束时桌上剩余的卡牌序列。
包含多组测试数据。
输入包含两行。
第一行包含一个整数n表示卡牌的数量。
第二行包含n个空格隔开的整数,表示排成一行的卡牌上对应的数字(取值范围[1,1000000000])。
n≤1000000
输出包含两行。
第一行包含一个整数表示游戏的取牌总轮次。
第二行包含游戏结束时桌上剩余的卡牌序列,用空格隔开。
7 5 6 3 7 4 1 2
2 5 6 7
用一个递增的栈a存最后剩下的元素,遇到大于等于a[top]的元素就入a栈,否则入b栈;栈b存a[i]和a[i-1]间会被拿
出的牌;
最大的问题在于如何求游戏的轮数,观察一下不难发现每张牌被抽出的轮数=该牌往前数几张牌才能遇到大于自己的牌;
但是要是真这样去做很容易wa或者tle(来自开始这样写,然后改了四个小时的怨念);嗯~ o(* ̄▽ ̄*)o正题来了:
用b来模拟抽牌,只不过不是像真正的游戏那样一轮一轮地抽,而是把a[i]和a[i-1]间的某区间牌先抽完,再继续处理后面
的,具体操作看代码;每次取最大的轮数。(轮数等于往前走遇到第一个大于该牌的区间中最大的轮数加一(因为要左边的牌
比自己大才会被抽走,所以要遇到比自己大的数之前的牌都被抽走,它才能被抽走))
a: 63 b: 2 c:1 //c存轮数
a: 63 b: 56 c:2
a: 63 b: 56 3 c:2 1
a: 63 b: 57 c:3
a: 63 b: 57 9 c:3 1
a: 63 b: 57 10 c:3 2
a: 63 b: 57 11 c:3 3
a: 63 b: 58 c:4
a: 63 b: 58 6 c:4 1
a: 63 b: 58 6 5 c:4 1 1a: 63 b: 58 20 c:4 2
#include<cstdio> #define MAXX 1000050 int a[MAXX]; int b[MAXX]; //单调栈 int c[MAXX]; //记录b[0]~b[topb]间牌被抽走的轮数 int main() { int n; while (scanf("%d", &n) != EOF) { int topa = -1; int topb = -1; int x; int max = 0; for (int i = 0; i < n; i++) { scanf("%d", &x); if (topa == -1) { topa++; a[topa] = x; } else if (a[topa] <= x) { topa++; a[topa] = x; topb = -1; } else { if (topb == -1) { topb++; b[topb] = x; c[topb] = 1; } else if (x < b[topb]) { topb++; b[topb] = x; c[topb] = 1; } else if (x >= b[topb]) { int t=-1; while (topb>-1&&x >= b[topb]) { if(c[topb]>t) t=c[topb]; topb--; } b[++topb] = x; c[topb] = t+1; if (c[topb] > max)max = c[topb]; } if (c[topb] > max)max = c[topb]; } } printf("%d\n", max); int flag = 1; for (int i = 0; i <= topa; i++) { if (flag)flag = 0; else printf(" "); printf("%d", a[i]); } if(!flag)printf("\n"); } }