<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SQYBI.com &#187; 代码</title>
	<atom:link href="http://sqybi.com/blog/archives/tag/%e4%bb%a3%e7%a0%81/feed" rel="self" type="application/rss+xml" />
	<link>http://sqybi.com/blog</link>
	<description>生活 技术 想法 记录每一个应该记录的瞬间</description>
	<lastBuildDate>Thu, 09 Sep 2010 14:27:32 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Everything is New</title>
		<link>http://sqybi.com/blog/archives/127</link>
		<comments>http://sqybi.com/blog/archives/127#comments</comments>
		<pubDate>Sun, 29 Mar 2009 12:51:23 +0000</pubDate>
		<dc:creator>sqybi</dc:creator>
				<category><![CDATA[About Computer]]></category>
		<category><![CDATA[[OI, ACM, etc]]]></category>
		<category><![CDATA[ACM]]></category>
		<category><![CDATA[代码]]></category>
		<category><![CDATA[用户名]]></category>

		<guid isPermaLink="false">http://sqybi.com/blog/archives/127</guid>
		<description><![CDATA[友情提示: 本文没有任何实用价值, 请自行决定是否观看本文, 或者直接拉到最后 (不过估计我写不了几行). 虽然说就要进大学了, 但是还没有个新的面貌啊. Blog 已经两周一次更新了, 足以体现现在我有多么懒. 整天玩游戏, 上校内, 切题, 过得毫无感觉. Boss 那儿给了不到 20 道二分图匹配的题, 竟然前两道题都没有 1Y, 我真弱智. 嗯还是说正题吧. 开始为 ACM 做准备了. 虽然听说了 SJTU 的 ACM 队很难进, 不过还是有一些憧憬的. 毕竟大一的同学里只有这些 OIer 在竞争, 虽然个个都是高手, 不过自己也不能就这么放弃. 然后呢, 现在很无聊, 于是想为以后的 ACM 准备个用户名. 主要是在各个 OJ 上的 sqybi 的刷题记录没办法清除, 所以换个用户好知道自己哪道题还没做过... 毕竟以前做的题现在不一定还会了. 现在在用 sqybicpp, 看着就难看. 所以自己准备了一些用户名, 欢迎留言告诉我哪个好, 或者有更好的创意也请告诉我... ssjqtyu [...]]]></description>
			<content:encoded><![CDATA[<p>友情提示: 本文没有任何实用价值, 请自行决定是否观看本文, 或者直接拉到最后 (不过估计我写不了几行).</p>
<p>虽然说就要进大学了, 但是还没有个新的面貌啊.</p>
<p>Blog 已经两周一次更新了, 足以体现现在我有多么懒. 整天玩游戏, 上校内, 切题, 过得毫无感觉.   <br />Boss 那儿给了不到 20 道二分图匹配的题, 竟然前两道题都没有 1Y, 我真弱智.</p>
<p>嗯还是说正题吧. 开始为 ACM 做准备了. 虽然听说了 SJTU 的 ACM 队很难进, 不过还是有一些憧憬的. 毕竟大一的同学里只有这些 OIer 在竞争, 虽然个个都是高手, 不过自己也不能就这么放弃.   <br />然后呢, 现在很无聊, 于是想为以后的 ACM 准备个用户名. 主要是在各个 OJ 上的 sqybi 的刷题记录没办法清除, 所以换个用户好知道自己哪道题还没做过... 毕竟以前做的题现在不一定还会了.    <br />现在在用 sqybicpp, 看着就难看. 所以自己准备了一些用户名, 欢迎留言告诉我哪个好, 或者有更好的创意也请告诉我...</p>
<p>ssjqtyu - sSjQtYu   <br />sqeezey - SQeezeY    <br />saqcym - SaQcYm    <br />crapixo - cRaPiXo -&gt; RPX -&gt; (R+1)(P+1)(X+1) -&gt; SQY    <br />sterizy - sTeRiZy -&gt; TRZ -&gt; (T-1)(R-1)(Z-1) -&gt; SQY</p>
<p>后面是解释.. 基本就围绕着 sqy, sjtu 和 acm. 貌似 crapixo 还比较好看... ssjqtyu 那个就很难看...</p>
<p>另外, 最近做题要顺手整理代码了, zmc 给我的那份代码库简直太强大了... 不过还是自己整理的代码用着舒服.</p>
]]></content:encoded>
			<wfw:commentRss>http://sqybi.com/blog/archives/127/feed</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>NOIP 2008 第四题 双栈排序(twostack) 题解</title>
		<link>http://sqybi.com/blog/archives/78</link>
		<comments>http://sqybi.com/blog/archives/78#comments</comments>
		<pubDate>Sun, 16 Nov 2008 09:14:11 +0000</pubDate>
		<dc:creator>sqybi</dc:creator>
				<category><![CDATA[About Computer]]></category>
		<category><![CDATA[[OI, ACM, etc]]]></category>
		<category><![CDATA[NOIP]]></category>
		<category><![CDATA[OI]]></category>
		<category><![CDATA[二分图]]></category>
		<category><![CDATA[代码]]></category>
		<category><![CDATA[图论]]></category>
		<category><![CDATA[排序]]></category>
		<category><![CDATA[栈]]></category>
		<category><![CDATA[证明]]></category>
		<category><![CDATA[队列]]></category>
		<category><![CDATA[题解]]></category>

		<guid isPermaLink="false">http://sqybi.com/blog/archives/78</guid>
		<description><![CDATA[这可能是第一份正确的完整题解. 这个帖子: 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&#60;=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&#60;j&#60;k且q1[k]&#60;q1[i]&#60;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]&#62;q1[i],这显然是不正确的. 接下来证明必要性.也就是,如果两个数不可以压入同一个栈,那么它们一定满足条件p.这里我们来证明它的逆否命题,也就是"如果不满足条件p,那么这两个数一定可以压入同一个栈." 不满足条件p有两种情况:一种是对于任意i&#60;j&#60;k且q1[i]&#60;q1[j],q1[k]&#62;q1[i];另一种是对于任意i&#60;j,q1[i]&#62;q1[j]. 第一种情况下,很显然,在q1[k]被压入栈的时候,q1[i]已经被弹出栈.那么,q1[k]不会对q1[j]产生任何影响(这里可能有点乱,因为看起来,当q1[j]&#60;q1[k]的时候,是会有影响的,但实际上,这还需要另一个数r,满足j&#60;k&#60;r且q1[r]&#60;q1[j]&#60;q1[k],也就是证明充分性的时候所说的情况…而事实上我们现在并不考虑这个r,所以说q1[k]对q1[j]没有影响). 第二种情况下,我们可以发现这其实就是一个降序序列,所以所有数字都可以压入同一个栈. 这样,原命题的逆否命题得证,所以原命题得证. 此时,条件p为q1[i]和q1[j]不能压入同一个栈的充要条件也得证. 这样,我们对所有的数对(i,j)满足1&#60;=i&#60;j&#60;=n,检查是否存在i&#60;j&#60;k满足p1[k]&#60;p1[i]&#60;p1[j].如果存在,那么在点i和点j之间连一条无向边,表示p1[i]和p1[j]不能压入同一个栈.此时想到了什么?那就是二分图~ 二分图的两部分看作两个栈,因为二分图的同一部分内不会出现任何连边,也就相当于不能压入同一个栈的所有结点都分到了两个栈中. 此时我们只考虑检查是否有解,所以只要O(n)检查出这个图是不是二分图,就可以得知是否有解. 此时,检查有解的问题已经解决.接下来的问题是,如何找到字典序最小的解. 实际上,可以发现,如果把二分图染成1和2两种颜色,那么结点染色为1对应当前结点被压入s1,为2对应被压入s2.为了字典序尽量小,我们希望让编号小的结点优先压入s1. 又发现二分图的不同连通分量之间的染色是互不影响的,所以可以每次选取一个未染色的编号最小的结点,将它染色为1并从它开始DFS染色,直到所有结点都被染色为止.这样,我们就得到了每个结点应该压入哪个栈中.接下来要做的,只不过是模拟之后输出序列啦~ 还有一点小问题,就是如果对于数对(i,j),都去枚举检查是否存在k使得p1[k]&#60;p1[i]&#60;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 [...]]]></description>
			<content:encoded><![CDATA[<p><span style="color: #ff0000;">这可能是第一份正确的完整题解.</span></p>
<p>这个帖子: <a href="http://www.oibh.org/bbs/thread-26983-1-2.html">http://www.oibh.org/bbs/thread-26983-1-2.html</a> 是此算法的来源,但是帖子中说的不是很清楚,而且没有证明.题解中将把这些问题解决.<br />
十分感谢inzaghi250(sqybi注:还好我不是inzaghi的fans…)提供的算法.</p>
<p>这道题大概可以归结为如下题意:<br />
有两个队列和两个栈,分别命名为队列1(q1),队列2(q2),栈1(s1)和栈2(s2).最初的时候,q2,s1和s2都为空,而q1中有n个数(n&lt;=1000),为1~n的某个排列.<br />
现在支持如下四种操作:<br />
a操作,将 q1的首元素提取出并加入s1的栈顶.<br />
b操作,将s1的栈顶元素弹出并加入<span style="text-decoration: line-through;">q1</span>q2的队列尾.<br />
c操作,将 q1的首元素提取出并加入s2的栈顶.<br />
d操作,将s2的栈顶元素弹出并加入<span style="text-decoration: line-through;">q1</span>q2的队列尾.<br />
请判断,是否可以经过一系列操作之后,使得q2中依次存储着1,2,3,…,n.如果可以,求出字典序最小的一个操作序列.</p>
<p>这道题的错误做法很多,错误做法却能得满分的也很多,这里就不多说了.直接切入正题,就是即将介绍的这个基于二分图的算法.<br />
注意到并没有说基于二分图匹配,因为这个算法和二分图匹配无关.这个算法只是用到了给一个图着色成二分图.</p>
<p>第一步需要解决的问题是,判断是否有解.</p>
<p>考虑对于任意两个数q1[i]和q1[j]来说,它们不能压入同一个栈中的充要条件是什么(注意没有必要使它们同时存在于同一个栈中,只是压入了同一个栈).实际上,这个条件p是:存在一个k,使得i&lt;j&lt;k且q1[k]&lt;q1[i]&lt;q1[j].</p>
<p>首先证明充分性,即如果满足条件p,那么这两个数一定不能压入同一个栈.这个结论很显然,使用反证法可证.<br />
假设这两个数压入了同一个栈,那么在压入q1[k]的时候栈内情况如下:<br />
…q1[i]…q1[j]…<br />
因为q1[k]比q1[i]和q1[j]都小,所以很显然,当q1[k]没有被弹出的时候,另外两个数也都不能被弹出(否则q2中的数字顺序就不是1,2,3,…,n了).<br />
而之后,无论其它的数字在什么时候被弹出,q1[j]总是会在q1[i]之前弹出.而q1[j]&gt;q1[i],这显然是不正确的.</p>
<p>接下来证明必要性.也就是,如果两个数不可以压入同一个栈,那么它们一定满足条件p.这里我们来证明它的逆否命题,也就是"如果不满足条件p,那么这两个数一定可以压入同一个栈."<br />
不满足条件p有两种情况:一种是对于任意i&lt;j&lt;k且q1[i]&lt;q1[j],q1[k]&gt;q1[i];另一种是对于任意i&lt;j,q1[i]&gt;q1[j].<br />
第一种情况下,很显然,在q1[k]被压入栈的时候,q1[i]已经被弹出栈.那么,q1[k]不会对q1[j]产生任何影响(这里可能有点乱,因为看起来,当q1[j]&lt;q1[k]的时候,是会有影响的,但实际上,这还需要另一个数r,满足j&lt;k&lt;r且q1[r]&lt;q1[j]&lt;q1[k],也就是证明充分性的时候所说的情况…而事实上我们现在并不考虑这个r,所以说q1[k]对q1[j]没有影响).<br />
第二种情况下,我们可以发现这其实就是一个降序序列,所以所有数字都可以压入同一个栈.<br />
这样,原命题的逆否命题得证,所以原命题得证.</p>
<p>此时,条件p为q1[i]和q1[j]不能压入同一个栈的充要条件也得证.</p>
<p>这样,我们对所有的数对(i,j)满足1&lt;=i&lt;j&lt;=n,检查是否存在i&lt;j&lt;k满足p1[k]&lt;p1[i]&lt;p1[j].如果存在,那么在点i和点j之间连一条无向边,表示p1[i]和p1[j]不能压入同一个栈.此时想到了什么?那就是二分图~<br />
二分图的两部分看作两个栈,因为二分图的同一部分内不会出现任何连边,也就相当于不能压入同一个栈的所有结点都分到了两个栈中.<br />
此时我们只考虑检查是否有解,所以只要O(n)检查出这个图是不是二分图,就可以得知是否有解.</p>
<p>此时,检查有解的问题已经解决.接下来的问题是,如何找到字典序最小的解.<br />
实际上,可以发现,如果把二分图染成1和2两种颜色,那么结点染色为1对应当前结点被压入s1,为2对应被压入s2.为了字典序尽量小,我们希望让编号小的结点优先压入s1.<br />
又发现二分图的不同连通分量之间的染色是互不影响的,所以可以每次选取一个未染色的编号最小的结点,将它染色为1并从它开始DFS染色,直到所有结点都被染色为止.这样,我们就得到了每个结点应该压入哪个栈中.接下来要做的,只不过是模拟之后输出序列啦~</p>
<p>还有一点小问题,就是如果对于数对(i,j),都去枚举检查是否存在k使得p1[k]&lt;p1[i]&lt;p1[j]的话,那么复杂度就升到了O(n^3).解决方法就是,首先预处理出数组b,b[i]表示从p1[i]到p1[n]中的最小值.接下来,只需要枚举所有数对(i,j),检查b[j+1]是否小于p1[i]且p1[i]是否小于p1[j]就可以了.</p>
<p>附代码(除去注释不到100行),带注释.代码中的a数组对应文中的队列p1.<br />
已经过掉所有标准数据,以及5 7 2 4 1 6 3这组让很多贪心程序挂掉的数据~</p>
<blockquote><p>xpycc  November 29th, 2008 at 09:24</p>
<p>这位大牛不妨试试以下数据：<br />
1000<br />
2 3 4 5 ... 1000 1<br />
对于这组数据 MS 您的程序崩溃了……</p>
<p>原因是这组数据中无向边的条数为 999*999 ，而您的内存只申请了 1002*2 。</p>
<p>sqybi  November 29th, 2008 at 11:29</p>
<p>@xpycc<br />
嗯...程序貌似是有bug的...<br />
而且判无解的地方貌似也有问题...</p></blockquote>
<pre lang="cpp">#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", &amp;n);
    for (int i = 1; i &lt;= n; ++ i) scanf("%d", &amp;a[i]);

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

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

    //DFS染色
    memset(color, 0, sizeof(color));
    result = true;
    for (int i = 1; i &lt;= 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 &lt;= 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 ++;
            }
        }
    }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://sqybi.com/blog/archives/78/feed</wfw:commentRss>
		<slash:comments>27</slash:comments>
		</item>
	</channel>
</rss>
