SQYBI.com

Change is a part of life, and takes part in finding us who we are.

2008年11月16日
by sqybi
28 Comments

NOIP 2008 第四题 双栈排序(twostack) 题解

这份题解的代码可能有错,请仅做参考。。。

这个帖子: http://www.oibh.org/bbs/thread-26983-1-2.html 是此算法的来源,但是帖子中说的不是很清楚,而且没有证明.题解中将把这些问题解决.
十分感谢inzaghi250(sqybi注:还好我不是inzaghi的fans…)提供的算法.

这道题大概可以归结为如下题意:
有两个队列和两个栈,分别命名为队列1(q1),队列2(q2),栈1(s1)和栈2(s2).最初的时候,q2,s1和s2都为空,而q1中有n个数(n<=1000),为1~n的某个排列.
现在支持如下四种操作:
a操作,将 q1的首元素提取出并加入s1的栈顶.
b操作,将s1的栈顶元素弹出并加入q1q2的队列尾.
c操作,将 q1的首元素提取出并加入s2的栈顶.
d操作,将s2的栈顶元素弹出并加入q1q2的队列尾.
请判断,是否可以经过一系列操作之后,使得q2中依次存储着1,2,3,…,n.如果可以,求出字典序最小的一个操作序列.

这道题的错误做法很多,错误做法却能得满分的也很多,这里就不多说了.直接切入正题,就是即将介绍的这个基于二分图的算法.
注意到并没有说基于二分图匹配,因为这个算法和二分图匹配无关.这个算法只是用到了给一个图着色成二分图.

第一步需要解决的问题是,判断是否有解.

考虑对于任意两个数q1[i]和q1[j]来说,它们不能压入同一个栈中的充要条件是什么(注意没有必要使它们同时存在于同一个栈中,只是压入了同一个栈).实际上,这个条件p是:存在一个k,使得i<j<k且q1[k]<q1[i]<q1[j].

首先证明充分性,即如果满足条件p,那么这两个数一定不能压入同一个栈.这个结论很显然,使用反证法可证.
假设这两个数压入了同一个栈,那么在压入q1[k]的时候栈内情况如下:
…q1[i]…q1[j]…
因为q1[k]比q1[i]和q1[j]都小,所以很显然,当q1[k]没有被弹出的时候,另外两个数也都不能被弹出(否则q2中的数字顺序就不是1,2,3,…,n了).
而之后,无论其它的数字在什么时候被弹出,q1[j]总是会在q1[i]之前弹出.而q1[j]>q1[i],这显然是不正确的.

接下来证明必要性.也就是,如果两个数不可以压入同一个栈,那么它们一定满足条件p.这里我们来证明它的逆否命题,也就是"如果不满足条件p,那么这两个数一定可以压入同一个栈."
不满足条件p有两种情况:一种是对于任意i<j<k且q1[i]<q1[j],q1[k]>q1[i];另一种是对于任意i<j,q1[i]>q1[j].
第一种情况下,很显然,在q1[k]被压入栈的时候,q1[i]已经被弹出栈.那么,q1[k]不会对q1[j]产生任何影响(这里可能有点乱,因为看起来,当q1[j]<q1[k]的时候,是会有影响的,但实际上,这还需要另一个数r,满足j<k<r且q1[r]<q1[j]<q1[k],也就是证明充分性的时候所说的情况…而事实上我们现在并不考虑这个r,所以说q1[k]对q1[j]没有影响).
第二种情况下,我们可以发现这其实就是一个降序序列,所以所有数字都可以压入同一个栈.
这样,原命题的逆否命题得证,所以原命题得证.

此时,条件p为q1[i]和q1[j]不能压入同一个栈的充要条件也得证.

这样,我们对所有的数对(i,j)满足1<=i<j<=n,检查是否存在i<j<k满足p1[k]<p1[i]<p1[j].如果存在,那么在点i和点j之间连一条无向边,表示p1[i]和p1[j]不能压入同一个栈.此时想到了什么?那就是二分图~
二分图的两部分看作两个栈,因为二分图的同一部分内不会出现任何连边,也就相当于不能压入同一个栈的所有结点都分到了两个栈中.
此时我们只考虑检查是否有解,所以只要O(n)检查出这个图是不是二分图,就可以得知是否有解.

此时,检查有解的问题已经解决.接下来的问题是,如何找到字典序最小的解.
实际上,可以发现,如果把二分图染成1和2两种颜色,那么结点染色为1对应当前结点被压入s1,为2对应被压入s2.为了字典序尽量小,我们希望让编号小的结点优先压入s1.
又发现二分图的不同连通分量之间的染色是互不影响的,所以可以每次选取一个未染色的编号最小的结点,将它染色为1并从它开始DFS染色,直到所有结点都被染色为止.这样,我们就得到了每个结点应该压入哪个栈中.接下来要做的,只不过是模拟之后输出序列啦~

还有一点小问题,就是如果对于数对(i,j),都去枚举检查是否存在k使得p1[k]<p1[i]<p1[j]的话,那么复杂度就升到了O(n^3).解决方法就是,首先预处理出数组b,b[i]表示从p1[i]到p1[n]中的最小值.接下来,只需要枚举所有数对(i,j),检查b[j+1]是否小于p1[i]且p1[i]是否小于p1[j]就可以了.

附代码(除去注释不到100行),带注释.代码中的a数组对应文中的队列p1.
已经过掉所有标准数据,以及5 7 2 4 1 6 3这组让很多贪心程序挂掉的数据~

xpycc  November 29th, 2008 at 09:24

这位大牛不妨试试以下数据:
1000
2 3 4 5 ... 1000 1
对于这组数据 MS 您的程序崩溃了……

原因是这组数据中无向边的条数为 999*999 ,而您的内存只申请了 1002*2 。

sqybi  November 29th, 2008 at 11:29

@xpycc
嗯...程序貌似是有bug的...
而且判无解的地方貌似也有问题...

#include

using namespace std;

const int nn = 1002, mm = nn * 2, inf = 1000000000;
int n, tot, now;
int a[nn], b[nn], head[nn], color[nn];
int adj[mm], next[mm];
int stack[3][nn];
bool result;

void addEdge(int x, int y) //加边
{
    ++ tot;
    adj[tot] = y;
    next[tot] = head[x];
    head[x] = tot;
}

bool dfs(int i) //DFS染色,检查图是否是二分图的经典算法
{
    int temp = head[i];
    while (temp) //邻接表,检查每一条边
    {
        if (! color[adj[temp]]) //如果与当前结点的结点还未染色
        {
            color[adj[temp]] = 3 - color[i]; //进行染色
            dfs(adj[temp]); //DFS
        }
        if (color[adj[temp]] == color[i]) return false;
            //如果两个相邻结点染色相同,说明此图不是二分图,返回无解
        temp = next[temp];
    }
    return true;
}

int main()
{
    freopen("twostack.in", "r", stdin);
    freopen("twostack.out", "w", stdout);

    //输入
    scanf("%d", &n);
    for (int i = 1; i <= n; ++ i) scanf("%d", &a[i]);

    //预处理b数组
    b[n + 1] = inf;
    for (int i = n; i >= 1; -- i) b[i] = min(b[i + 1], a[i]); //"min" in STL

    //枚举数对(i,j)并加边
    tot = 0;
    for (int i = 1; i <= n; ++ i)
        for (int j = i + 1; j <= n; ++ j)
            if (b[j + 1] < a[i] && a[i] < a[j])
            {
                addEdge(i, j);
                addEdge(j, i);
            }

    //DFS染色
    memset(color, 0, sizeof(color));
    result = true;
    for (int i = 1; i <= n; ++ i) //每次找当前未染色的编号最小的结点,并染颜色1
        if (! color[i]) //当前位置尚未被染色
        {
            color[i] = 1;
            if (! dfs(i)) //染色时出现矛盾,此时图不是一个二分图,即无法分配到两个栈中
            {
                result = false; //记录无解
                break;
            }
        }

    if (! result) //无解
        printf("0");
    else //有解
    {
        //模拟求解
        now = 1;
        for (int i = 1; i <= n; ++ i)
        {
            //将当前数字压入对应的栈
            if (color[i] == 1)
                printf("a ");
            else
                printf("c ");
            stack[color[i]][0] ++;
            stack[color[i]][stack[color[i]][0]] = a[i]; //this will work even if stack[1][0] = 0

            //循环检查,如果可以的话就从栈顶弹出元素
            while (stack[1][stack[1][0]] == now || stack[2][stack[2][0]] == now)
            {
                if (stack[1][stack[1][0]] == now)
                {
                    printf("b ");
                    stack[1][0] --;
                }
                else if (stack[2][stack[2][0]] == now)
                {
                    printf("d ");
                    stack[2][0] --;
                }
                now ++;
            }
        }
    }
}

2008年11月14日
by sqybi
8 Comments

NOIP 2008, fighting

一眨眼都到NOIP 2008了,自己都成前辈了啊.

这次没参赛,明天会到场bless各位的,特别是zmc同学和wr同学~szy和zch就算了吧,你们俩都有1=了...
还有raulliubo,吴豪等人,因为路途遥远无法到场(^_^),在此先bless了~dog你还年轻,先等等咯.

明天要搞题,争取至少搞定三道题吧...
有种很强烈的感觉,这次就一道DP.

另外借鱼牛的一句话,"祝各NOIP选手考出真实的好成绩".
相信不会再看到作弊事件的.净化OI,人人有责...

明天还要早起赶过去,不多说咯.
估计明年的这个时候我就不会写NOIP2009祝福文了吧...

Fighting~

2008年11月13日
by sqybi
4 Comments

SRM 426

好久没写SRM的文章了(上一次还是这里的SRM 422吧...),这次写主要是因为终于重新变黄了.
数一数,都蓝了快10场了呢.

上一场没参加,但这场开始的时候rp也没有爆发.
不知道怎么,12点开始的比赛,闹钟竟然也设在了12点.匆匆忙忙爬起来,都没来及洗脸就坐下开始看题.结果一刻钟都没看懂题,orz.
然后擦了一把脸接着看,终于看懂了.按照惯例,第一题应该是裸search的,但是自己分析复杂度竟然太高.只好求助于FancyMouse牛(后面那道500pts也求助于FancyMouse牛了),后来得知原先4^n的复杂度实际应该是3^n,然后后来又优化了几次,终于在比赛还有半个多小时的时候搞定250pts,这时这道题还剩下120多pts了.

第二题先和FM牛叙述了题意,然后自己感觉还是裸search.于是问FM牛,和我的想法一样.还有半小时,本来不想写了,但是在FM牛的鼓励下还是写了.最后在比赛还有2min结束的时候搞定500pts,200多分.

1000pts看了就知道不是能做的题,最后的确是这样.只有Petr和另一位大牛两人搞定1000pts,剩下的都挂掉了.
最后rating涨的也不是很多,要是250pts快点搞定就说不定会涨很多了.

另外祝贺一下RoBa神牛又一次变红.

附一段聊天记录(有删减):

Killa.sqybi 0:40:07
还有半小时...我估计自己写不完500..
Fancy Mouse 0:40:20
MA。。。impossible is nothing
Fancy Mouse 0:40:49
自从上次regional最后15分钟切掉2题以后偶十分信奉这句话
Fancy Mouse 0:41:15
虽然其中一道是别的队2hr以内就切掉的水题
Fancy Mouse 0:41:23
偶们不知怎么就是不会做
Killa.sqybi 0:41:49
呃...
Fancy Mouse 0:41:55
偶在前面那道搜索切掉以后还剩下10分钟的时候豁出去写了个暴力居然过掉了

再次感谢FM牛...

2008年11月06日
by sqybi
2 Comments

USACO Contest October 2008 Qualifying Round (OCT08) 翻译

很抱歉这么晚才放出来,翻译早就做好了,但是根本就都忘掉了.
很无奈地放一个more标签,想看的自己点开,因为比较长,放在主页上比较难看...
今天SRM竟然被我忘了,现在登陆又不知道为啥登不进去,本来以为SRM还能更新一篇文章的,看起来没戏了...

Continue Reading →

2008年11月05日
by sqybi
26 Comments

发生在我身上的作弊事件

首先承认,我标题党了.
今天忘了带本本的电源线回家,借着电池写一点点,希望能坚持到写完甚至看几集柯南.

刚刚收到了来自某同学A的短信.说是ta的另一个同学B,学习成绩不好,高考可能连天大都考不上.大家又知道最近NOIP快到了,于是乎就不知道怎么打听到了我,想让我帮他把题目做出来然后拿短信发过去.
我自然是一口回绝了.

我和A并不是很熟识,但是我很确定A并不是一个多么坏的人,或者说是一个好孩子.其实对A帮B来求我的行为,也是挺理解的.理解归理解,让我同意这件事是肯定不可能的了.
也正因为这样,今天的文章里没有提到任何一个人的名字甚至与ta相关的一点资料.否则直接拉出来曝光,俩人就都会很惨.我不想那样,正如当时xw事件一样,每个人都不坏,不希望把谁一棒子打死.
我回复A,如果B现在拼高考,也不是没时间,估计以前B也把时间都花在玩游戏上了.
A只是回复我,ta当时的第一反应也是感到愤慨.

几年的经验告诉我,OI并不是对所有人都适合.毕竟是功利性太强的东西.
自己也是一个天津OI的前辈了,曾经看着身边的人们一个一个地倒下,最后所剩无几.最初的没感觉,到后来的害怕,到最后的没感觉.一个可怕的轮回.
那些倒下的人,并不是被谁谁谁打倒的.打倒他们的是自己,是自己的功利.

不得不承认的是,我搞OI也很功利.我一直很坚信,如果没有保送,我一定会扭头回去拼高考(这里说一下,请不要回复什么大学不是唯一的出路以及此类的文笔更好一些的语言).但实际上,我是很享受coding的感觉的.如果啥时候coder不是民工了,能赚钱了,我一定会去做一个coder.
但是见过很多,被功利这颗烟雷迷惑的人,就像CS里那些弱智bot一样.

想翻一篇貌似是WTommy写的blog,是写他在负责NOIP小学组评测的时候,和NOIP小学组的学生们的对话.但是没翻出来,只好凭着记忆打一些东西到这里.
"我给你钱了,你就得给我测出分来."
-"为啥我没有分?" -"因为你的程序不对啊." -"给我点分吧." -"测出来没有分就没有了." -"可是我有钱啊."
请注意我加粗的那个词.

这个泥潭太深了,害怕自己有一天也会陷进去.但是,至少在现在还没有走出那一步的时候,多拉出来几个人吧.说不定,等到那一天,会有人把我拉出来的.

突然又想到了,一个最近看到的问题.
"国人的思想,有很多都是被别人灌输的,而没有自己的思想."
不论这句话对错,一些父母想维护自己的"权威",就已经是在做这件事了.很难想像,以后的人们都是一个个傀儡,自然也不愿去想象.

好了,回到正题.
我拒绝了A的请求,并不是像A所说的"捍卫这个比赛的公平性"这样伟大.只是觉得,抵制作弊这种事情人人都应该做到的,自己没有理由不去做.
或许如果没有xw事件,没有WTommy的那篇blog,我就会答应下来了呢.特别是xw事件,对我的影响很大.
记得最开始,许多人不相信,那封联名信能够起到什么作用.甚至OIBH上有帖子,讨论那封联名信是否会影响到在上面签字的人.
但是结果是,CCF重视了这次事件,最终给了我们一个比较满意的答复.于是,我从此开始信任身边的人们.
而当时在那封信上的签名,就成为了一种责任.既然亲手净化了天津OI,就不能自己再来污染它.

如果A看了这篇文章,希望你能看到上面这句话.这才是我拒绝你的真正原因.

附:当时在联名信上签名的人:
武斌(skywind,OIBH管理员,现武汉大学学生,对那次事件的解决做出了极大贡献) 杨博洋 邱堃 王乃岩(winsty,浙大学生,我哥) 朱健维 杨宗衡 段岩 李博 邹蒙川(ddeggzmc,当时的乐山外国语学校学生,现天津一中学生,很可爱的MM) 隋清宇(sqybi,本人) 刘智猷 王洋 秦黛(耀华学长) 康旭阳(SweetSc,新华中学学生) 卢光辉(WindyWinter,也是学长了,很有正义感和自己的思想的人) 张潇 舒昱扬

好吧,就侃到这里.貌似还能看两集柯南的说.
明天SRM, gl.

P.S.貌似现在的电量只能看一集了...