NOIP 2008 Senior 4

来源:互联网 发布:昆明暴恐 中国公知 编辑:程序博客网 时间:2024/06/07 11:54

传送门

这道题可以说难点有两个。一是证明在何种条件下两个数不能进入同一个栈,二是如何贪心使字典序最小并且能够正确写出代码(样例太坑了,只用了一个栈)。

对于第一个问题,我们发现,如果存在 大的 在 小的 的后面,并且有一个 更小的 在后面,那么 大的 就不能和 小的 在同一个栈中,否则就可以。因为我们要先输出那个 更小的,就势必要先把 大的 和 小的 入栈,且在输出 更小的 之前这两个都没有出栈。所以 大的 和 小的 一定不能在同一个栈中,否则一定会先输出大的,后输出小的。
以上是一个充分性证明,但还没有必要性证明,即我们还没有证明“否则就可以”。这里我就给个写得很厉害的链接,自己去看看吧。。。考场上如果你能找到充分性证明的话,可以算是成功了一大半了。
传送门

然后,我们就可以把不能在同一个栈的元素跑一次二分图染色。如果跑出来的结果不是二分图,那么问题无解。否则问题就有解。

接下来,我们就要考虑生成字典序最小的操作序列了。首先,我们肯定得先考虑用第一个栈。但是怎么判断呢?由于我们染色时是优先染的黑色的(假设分为黑色和白色),那么我们就让黑色放到栈1,白色放到栈2。入栈的前提条件就多了一个颜色相互匹配。
但有可能着急地把一个元素放进栈中,导致一个该出栈的元素被压在了下面。所以我们要先检查是否该出栈了,再考虑入栈的事情。
最后还有一个坑。如果此时栈1可以出栈,而新元素可以进栈2,那么我们要先让栈1出栈。

参考代码

#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <string>#include <stack>#include <queue>#include <deque>#include <map>#include <set>#include <bitset>using std::cin;using std::cout;using std::endl;typedef int INT;inline INT readIn(){    INT a = 0;    bool minus = false;    char ch = getchar();    while (!(ch == '-' || ch >= '0' && ch <= '9')) ch = getchar();    if (ch == '-')    {        minus = true;        ch = getchar();    }    while (ch >= '0' && ch <= '9')    {        a *= 10;        a += ch;        a -= '0';        ch = getchar();    }    if (minus) a = -a;    return a;}const INT INF = (~(INT(1) << (sizeof(INT) * 8 - 1)));const INT maxn = 1005;INT n;INT a[maxn];std::vector<std::vector<INT> > edges;INT vis[maxn];bool dfs(INT node, INT color){    vis[node] = color;    for(int i = 0; i < edges[node].size(); i++)    {        INT to = edges[node][i];        if(vis[to] == color) return false;        else if(vis[to] == !color) continue;        else if(!dfs(to, !color)) return false;    }    return true;}void run(){    n = readIn();    for(int i = 1; i <= n; i++)    {        a[i] = readIn();    }    edges.resize(n + 1);    INT minVal = INF;    for(int i = n; i >= 1; i--)    {        for(int j = 1; j < i; j++)        {            if(a[j] < a[i] && minVal < a[j])            {                edges[j].push_back(i);                edges[i].push_back(j);            }        }        minVal = std::min(minVal, a[i]);    }    bool bOk = true;    memset(vis, -1, sizeof(vis));    for(int i = 1; i <= n; i++)    {        if(!~vis[i])        {            bOk = dfs(i, 0);            if(!bOk) break;        }    }    if(!bOk)    {        cout << 0 << endl;        return;    }    INT input = 1;    INT output = 1;    std::stack<INT> s1, s2;    s1.push(a[1]);    input++;    cout << "a";    while(output <= n)    {        if(!s1.empty() && s1.top() == output)        {            s1.pop();            output++;            cout << " b";        }        else if(!vis[input] && (s1.empty() || s1.top() > a[input]))        {            s1.push(a[input]);            input++;            cout << " a";        }        else if(!s2.empty() && s2.top() == output)        {            s2.pop();            output++;            cout << " d";        }        else if(vis[input] && (s2.empty() || s2.top() > a[input]))        {            s2.push(a[input]);            input++;            cout << " c";        }    }}int main(){    run();    return 0;}