【BZOJ2208/Jsoi2010】連通數
阿新 • • 發佈:2018-12-09
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;
}