美團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;
}
相關文章
相關標籤/搜索