您的位置:首页 > 其它

POJ3660 Cow Contests,TYVJ2024 运动员身高

2016-04-01 15:54 281 查看

Bitset Closure

poj3660

Memory: 388K Time: 16MS

https://code.csdn.net/snippets/1631904

tyvj2024

消耗时间 46 ms 消耗内存 1000 KiB

https://code.csdn.net/snippets/1631905

概述

这两道题基本完全一样,具体题意可以直接看tyvj上的汉语题

tyvj代码直接从poj上改的,增大maxn到2000,再添加一个数组当队列记录方案即可。

算法

//以下针对tyvj

只要满足 比i高的人数+比i矮的人数+1=n,那么这个人就可以被确定。

如果把A比B高看作从A->B连一条有向边,则X比Y高的充要条件就是X能到达Y。

所以比i高的人数就是能到达i的人数,比i矮的人数就是i能到达的人数(均不包括i),这可以通过看联通矩阵[…][i]和[i][…](在代码
38
行到
42
行)确定

怎么求联通矩阵?这就是传递闭包问题了。

化用Floyd:

for(int k=1; k<=n; ++k)
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
reach[i][j]|=(reach[i][k]&reach[k][j]);


这就足以通过这道题了

不过可以有一个常数优化

可以发现,前两层循环k, i以后,
reach[i][k]
就能确定了。如果它是false,那后面
reach[i][k]&reach[k][j]
肯定也是假,不用再循环了,直接
continue


代码变成了:

for(int k=1; k<=n; ++k)
for(int i=1; i<=n; ++i)
{
if(!reach[i][k]) continue;
for(int j=1; j<=n; ++j)
reach[i][j]|=reach[k][j];
}


然后,看到内层循环是把reach[i]和reach[k]按位或,而reach[x]可以看作一个行0-1向量组,就一个长二进制数

于是,把原来的
bool reach[maxn][maxn];
直接改成
bitset<maxn> reach[maxn];


代码变成:

for(int k=1; k<=n; ++k)
for(int i=1; i<=n; ++i)
if(reach[i][k])
reach[i]|=reach[k];


这样做不仅巨省空间(一字节一个bool变成一位一bool,空间复杂度大约是原来的18),而且省时间(以前一位一位手动或运算,现在伟大的
bitset
帮助我们拆成一个个int按位或)

时:O(132n3) ,空:O(18n2) (这俩算对了么?求验证)

其他的算法

来自tyvj题解(等待研究)

考虑到n只有1000,所以建两个图,一个原图,一个反向图,对于一个点i,做两次dfs,即可求出比i高和比i矮的人数,验证一下就可以了,复杂度O(n^2)

这位大神能不能再具体一点..还有,复杂度真的正常吗..

遇到的问题

一开始做tyvj上那道题,按自己的思路写了一个程序,总是WA最后一个点

我的思路:拓扑排序,如果没有入边的点只有一个,则这个点一定可以确定,删除这个点,加入答案,如果没有出边的点只有一个,也删除这个点,加入答案,如此循环直到不再能删下去为止。

多亏最后一个点啊(T_T)否则我真发现不了这个算法的漏洞

画了一张图解释下:



如图所示,不存在唯一出度为零的点,也不存在唯一入度为零的点,算法结束。但是中间蓝色的点,很明显无论左边两个点谁先谁后,都在它前面,它肯定是第4名。如果按照正确的思路,中间三个点因为都满足比它大的和比它小的点把它稳定地“夹”起来,所以它们都是可以确定的点。

谨此。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj bitset