luogu1155 雙棧排序
阿新 • • 發佈:2018-10-14
題目 所有 spa har col 嘗試 empty 進行 tor
題目大意
運用兩個棧的push和pop操作使得一個序列單調遞增且操作字典序最小。$n\leq 1000$。
題解
本題我們要嘗試運用“瞪眼法”,也就是推樣例。我們顯然要數字盡可能地推入第一個棧。那麽問題就是:怎樣的兩個數字不可以在同一個棧中呢?這樣的效果是:當一個數字a想要出棧時,其上端有個被他大的數字b擋著,且是不得不擋著。怎麽會“不得不”呢?那是因為有一個數字c<a在b的上面(原序列中,c在b的右面),因為要想使輸出序列遞增,必須把b入了棧以後才能出棧。所以,a和c不能共存。將所有滿足a、c這樣的條件的點連邊,進行二分圖染色(進入棧的編號)(染不了色輸出-1),然後模擬即可。
#include <cstdio> #include <cstring> #include <algorithm> #include <stack> #include <vector> using namespace std; const int MAX_NODE = 1010, MAX_EDGE = MAX_NODE * MAX_NODE; vector<char> Ops; struct Node; struct Edge; struct Node { Edge *Head; int Color; }_nodes[MAX_NODE]; int TotNode; Node *A[MAX_NODE]; stack<Node*> St[3]; struct Edge { Node *To; Edge *Next; }_edges[MAX_EDGE]; int _eCount; void Dfs(Node *cur, int color) { if (cur->Color && cur->Color != color) { printf("0\n"); exit(0); } if (cur->Color) return; cur->Color = color; for (Edge *e = cur->Head; e; e = e->Next) Dfs(e->To, color == 1 ? 2 : 1); } void AddEdge(Node *from, Node *to) { Edge *e = _edges + ++_eCount; e->To = to; e->Next = from->Head; from->Head = e; } void Build(Node *u, Node *v) { AddEdge(u, v); AddEdge(v, u); } void BuildGraph() { static Node *AftMinV[MAX_NODE]; AftMinV[TotNode] = A[TotNode]; for (int i = TotNode - 1; i >= 1; i--) AftMinV[i] = min(A[i], AftMinV[i + 1]); for (int i = 1; i <= TotNode; i++) for (int j = i + 1; j <= TotNode; j++) if (A[i] < A[j] && AftMinV[j] < A[i]) Build(A[i], A[j]); } int main() { scanf("%d", &TotNode); for (int i = 1; i <= TotNode; i++) { int vId; scanf("%d", &vId); A[i] = _nodes + vId; } BuildGraph(); for (int i = 1; i <= TotNode; i++) if (!A[i]->Color) Dfs(A[i], 1); Node *cur = _nodes + 1; for (int i = 1; i <= TotNode; i++) { Ops.push_back(A[i]->Color == 1 ? ‘a‘ : ‘c‘); St[A[i]->Color].push(A[i]); while (!St[cur->Color].empty() && St[cur->Color].top() == cur) { St[cur->Color].pop(); Ops.push_back(cur->Color == 1 ? ‘b‘ : ‘d‘); cur++; } } for (unsigned int i = 0; i < Ops.size(); i++) printf("%c ", Ops[i]); printf("\n"); return 0; }
luogu1155 雙棧排序