美团codeM之美团代金券

前天作了下美团的一个codeM比赛的资格赛,遇到一个题目挺有意思的,因此如今作一下总结。spa

题目描述

美团的每个用户都有一个用户代金券的消费记录日志,每位用户都能购买若干种代金券,可是每一种代金券最多只能购买一张。若是如今手上已经有了一张某种的代金券,那么只有在消费完这张代金券以后才能再次购买同种代金券。每位用户的购买和消费记录都记录在日志文件中,字符I表明购买代金券,字符O表明消费代金券。如今因为系统出现故障,形成了日志文件的损坏,在这个文件中有些记录没法恢复,只能用字符?代替,如今须要你来检查这个日志文件中的记录是不是合理的,若是出现某些冲突则表示该日志文件出现错误。
例如在某位用户的消费记录日志中的记录是这样的:日志

I 1
O 1

表明用户首先购买了编号为1的代金券,而后又消费了该代金券,因此该日志文件是正确的。对于正确的日志文件咱们输出-1。
若是消费记录是这样的:code

I 1
I 1
O 1

由于用户重复购买了编号为1的代金券,这是不合理的,因此这个日志文件有问题,问题出如今第二行,因此咱们应该输出2。
若是消费记录是这样的:get

?
I 1
O 2

由于这个?是没法恢复的,因此能够看作是任何可能的记录,在这个例子中咱们能够将它视为I 2,在这样的解释之下,这个日志文件是合理的,因此咱们最终的输出是-1。string

输入样例

首先咱们须要输入一个m,代表这个日志文件的总的行数。以后就像上述的例子那样的输入。it

2
I 1
O 1
3
I 1
I 1
O 1
3
?
I 1
O 2
4
I 1
?
O 1
O 1

输出样例

若是日志文件没有问题那么输出-1,若是有问题则输出最先出现问题的行数。io

-1
2
-1
4

解题思路

当时没有想明白具体的逻辑,改了好久都没有过,今天问小崔才明白正确的解题思路。通过分析以后咱们知道,输入?的时候是确定不会有错误的,错误只能出如今IO的某行。那么当输入为I number的时候咱们须要检查咱们是否已经拥有这张代金券,若是没有那么这条记录是没有问题的,若是已经购买了这张代金券咱们就应该进一步的检查,看上一次编号为number的代金券是何时买的,若是在这之间有一个?那么咱们能够将这个?做为一个O number的记录,这样也就不会产生错误了。一样当输入为O number的时候咱们一样按照相同的思路进行处理,若是有问题咱们就应该进一步检查上一个相同编号的消费记录在何时,在这段时间内是否有?,若是有?咱们就能够进行合理解释,不然就是一个错误的消费记录。美团

解题代码

#include <cstdio>
#include <set>
#include <cstring>
using namespace std;
const int maxn = 100000+10;

int m, number;
char type;
int buy_time[maxn], sale_time[maxn];
bool coupon[maxn];
set<int> unsure;
set<int>::iterator idx;
int main() {
    while (~scanf("%d", &m)) {
        memset(buy_time, -1, sizeof(buy_time));
        memset(sale_time, -1, sizeof(sale_time));
        memset(coupon, false, sizeof(coupon));
        unsure.clear();
        int ans = -1;
        for (int i = 1; i <= m; ++i) {
            getchar();
            scanf("%c", &type);
            if (type == '?') {
                unsure.insert(i);
            } else {
                scanf("%d", &number);
                if (type == 'I') {
                    if (!coupon[number]) {
                        coupon[number] = true;
                    } else {
                        // 对处于上一个I与如今这个I之间的第一个?作一个处理,将它做为一个O使用
                        int preBuyTime = buy_time[number];
                        idx = unsure.lower_bound(preBuyTime); // 返回preBuyTime以后的第一个?的位置
                        if (idx != unsure.end()) {
                            unsure.erase(idx);
                        } else {
                            // 若是没有?能够用,那么就是错误状况了
                            if (ans == -1) {
                                ans = i;
                            }
                        }
                    }
                    buy_time[number] = i;
                } else {
                    if (coupon[number]) {
                        coupon[number] = false;
                    } else {
                        // 对处于上一个O与如今这个O之间的第一个?作一个处理,将它做为一个I使用
                        int preSaleTime = sale_time[number];
                        idx = unsure.lower_bound(preSaleTime); // 返回preSaleTime以后的第一个?的位置
                        if (idx != unsure.end()) {
                            unsure.erase(idx);
                        } else {
                            // 若是没有?能够用,那么就是错误状况了
                            if (ans == -1) {
                                ans = i;
                            }
                        }
                    }
                    sale_time[number] = i;
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}
相关文章
相关标签/搜索