Floyd传递闭包-实用小知识
闭包是什么呢?
事实上牠可以被理解为一个关系网络中的关系,你认为牠是一个图上的边
我们要解决的问题就是,利用已知条件,想办法把点之间的关系尽可能多地确定出来
先举个例题作为例子
Luogu P2419 [USACO08JAN]Cow Contest S
题意概括:我们有 n 头牛,知道 m 个二元组关系( x , y ),代表 x 号牛的OI比 y 号牛强,需要我们输出我们最多可以确定几头牛的排名
显然,只有确定与其牠所有牛的关系,我们才能确定第 i 号牛的排名
为了做到这一点,我们只能通过点和点之间的关系进行转移,会想到一个熟悉的算法,Floyd
原来 \(d[x][y]\)代表的是点\(x\)和\(y\)之间的最短路长度,现在我们把牠改为\(x\)和\(y\)之间的关系是否确定
\(若确定,则d[x][y]=1,否则d[x][y]=0\)
参照Floyd原来的代码,写出以下程序
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]|=d[i][k] & d[k][j];
这一波就把所有可以确定的关系确定了
然后我们来看这题,我们重新设计一下d
\(若x比y强,d[x][y]=1 ; 否则,d[x][y]=0\)
然后就是和上边一样的处理关系
详见代码
#include <bits/stdc++.h>
using namespace std;
const int N=110;
bool g[N][N];
int n,m;
void floyd()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
g[i][j] |= g[i][k] & g[k][j];
}
int main()
{
cin>>n>>m;
while(m--)
{
int a,b;
cin>>a>>b;
g[a][b]=true;
}
floyd();
int res=0;
for(int i=1;i<=n;i++)
{
int cnt=0;//代表已经确定的关系数量
for(int j=1;j<=n;j++)
if(g[i][j] || g[j][i])
cnt++;
if(cnt == n-1) res++;//和所有节点都确定了就可以确定牠的排名
}
cout<<res<<endl;
return 0;
}
板子题做完了,来一道高级经验题
Luogu P4306 [JSOI2010]连通数
题意:给你一个有向图,求出每个节点能到达的节点数之和
显然这道题也是求闭包,但是数据是\(N<=2000\),显然\(O(n^3)\)的算法是无法跑过的
但是结合求闭包需要用到许多位运算,我们就想到了bitset
(不知道的自行百度吧)
由于bitset自带一个1/32的常数优化,使我们优雅而轻松地水掉了这题
#include<bits/stdc++.h>
using namespace std;
const int N=2010;
bitset<N> d[N];
string a;
int n,m,ans;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a;
for(int j=1;j<=n;j++)
if(a[j-1]=='1')
d[i][j]=1;
d[i][i]=1;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
if(d[i][k])
d[i]|=d[k];
for(int i=1;i<=n;i++)
ans+=d[i].count();
printf("%d",ans);
return 0;
}
本文来自博客园,作者:羊扬羊,转载请注明原文链接:https://www.cnblogs.com/sheepcsy/p/16487785.html