2208: [Jsoi2010]连通数
Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 4160 Solved: 1794
Description
Input
输入数据第一行是图顶点的数量,一个正整数N。 接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。
Output
输出一行一个整数,表示该图的连通数。
Sample Input
3
010
001
100
Sample Output
9
HINT
对于100%的数据,N不超过2000。
解析:
这道题数据是真的水而且做法也很多。。。
最先看到这道题感觉数据范围挺小的,感觉暴力(实际上最坏)没问题,结果还真能过。。。
但是我们不能仅仅满足于AC对吧,所以思考一下正解。
首先强连通缩点,重新简图,于是答案就为:
前提条件是连通块能到达于连通块。
如何算出连通块之间的连通性?直接用Floyd传递闭包,但是最好用压位能跑得更快。
当然也有人缩完点后用拓扑序递推复杂度应该更优秀一些,还有人不缩点直接用压位跑Floyd也能过而且速度也不慢。。。
搜索(最慢但最好想):
#include <bits/stdc++.h>
using namespace std;
const int Max=2010;
int n,m,ans,size;
int first[Max],vis[Max];
char ch[Max];
struct shu{int to,next;};
shu edge[Max*Max];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') f=-1,c=getchar();
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void build(int x,int y)
{
edge[++size].next=first[x];
first[x]=size;
edge[size].to=y;
}
inline void dfs(int point)
{
if(!vis[point]) ans++,vis[point]=1;
for(int u=first[point];u;u=edge[u].next)
{
int to=edge[u].to;
if(vis[to]) continue;
dfs(to);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",ch+1);
for(int j=1;j<=n;j++) if(ch[j]=='1') build(i,j);
}
for(int i=1;i<=n;i++)
{
dfs(i);
memset(vis,0,sizeof(vis));
}
cout<<ans<<"\n";
return 0;
}
直接压位Floyd(最短速度适中):
//次代码源自https://blog.csdn.net/linkfqy/article/details/75578669
#include<cstdio>
#include<bitset>
using namespace std;
const int maxn=2005;
int n,ans;
char s[maxn];
bitset<maxn> f[maxn];
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++){
scanf("%s",s);
for (int j=1;j<=n;j++)
if (s[j-1]=='1'||i==j) f[i][j]=1;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (f[j].test(i)) f[j]|=f[i];
for (int i=1;i<=n;i++) ans+=f[i].count();
printf("%d",ans);
return 0;
}
缩点+压位Floyd(速度较快):
#include <bits/stdc++.h>
using namespace std;
const int Max=2005;
int n,m,size,cnt,tot,Index,ans;
int first[Max],low[Max],num[Max],p[Max],vis[Max],sum[Max],father[Max];
char ch[Max];
struct shu{int to,next;};
shu edge[Max*Max];
bitset<2005>f[Max];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') f=-1,c=getchar();
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void build(int x,int y)
{
edge[++size].next=first[x];
first[x]=size;
edge[size].to=y;
}
inline void tarjan(int point)
{
num[point]=low[point]=++Index;
p[++tot]=point,vis[point]=1;
for(int u=first[point];u;u=edge[u].next)
{
int to=edge[u].to;
if(!num[to]) tarjan(to),low[point]=min(low[point],low[to]);
else if(vis[to]) low[point]=min(num[to],low[point]);
}
if(low[point]==num[point])
{
cnt++;
while(1)
{
int x=p[tot--];
father[x]=cnt,vis[x]=0,sum[cnt]++;
if(x==point) break;
}
}
}
inline void solve()
{
size=0;
for(int i=1;i<=cnt;i++) f[i][i]=1;
for(int i=1;i<=n;i++)
for(int u=first[i];u;u=edge[u].next)
f[father[i]][father[edge[u].to]]=1;
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
if(f[i][j]) f[i]|=f[j];
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
if(f[i][j]) ans+=sum[i]*sum[j];
}
int main()
{
n=get_int();
for(int i=1;i<=n;i++)
{
scanf("%s",ch+1);
for(int j=1;j<=n;j++) if(ch[j]=='1') build(i,j);
}
for(int i=1;i<=n;i++) if(!num[i]) tarjan(i);
solve();
cout<<ans<<"\n";
return 0;
}