题目描述

输入格式

输入数据第一行是图顶点的数量,一个正整数N。 接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。

输出格式

输出一行一个整数,表示该图的连通数。

样例

样例输入

  1. 3
  2. 010
  3. 001
  4. 100

样例输出

  1. 9

数据范围与提示

对于100%的数据,N不超过2000。

solution:

这道题给出三种算法:

DFS:

这道题搜索可以过

用vecter建边,若有一条由i指向j的边,那么把j压到i的vector中(这种建边方法好像比前向星快)

建立bool数组vis,vis[i][j]=1表示已经访问过由i指向j的边

我们对于每一个点进行dfs,其中dfs(i,j)表示以i为起点开始搜索,当前搜到了j点,依次向下dfs并统计答案

由于这里的vis存的是边的信息,所以不用考虑环。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<algorithm>
  6. #include<queue>
  7. #include<vector>
  8. #define MAXN 2005
  9. using namespace std;
  10. int n,ans[MAXN],res=;
  11. char ch[MAXN][MAXN];
  12. vector<int>mapa[MAXN];
  13. bool vis[MAXN][MAXN];//vis[i][j]表示是否访问过由i向j的边
  14. void dfs(int st,int now){//st:起点,now:当前节点
  15. ans[st]++;
  16. vis[st][now]=;
  17. int m=mapa[now].size();
  18. for(int i=;i<m;i++){
  19. if(!vis[st][mapa[now][i]])
  20. dfs(st,mapa[now][i]);
  21. }
  22. }
  23. int main(){
  24. scanf("%d",&n);
  25. for(int i=;i<=n;i++){
  26. scanf("%s",ch[i]+);
  27. for(int j=;j<=n;j++){
  28. if(ch[i][j]=='')
  29. mapa[i].push_back(j);
  30. //cout<<ch[i][j];
  31. }
  32. //cout<<endl;
  33. }
  34. for(int i=;i<=n;i++)
  35. dfs(i,i),res+=ans[i];
  36. printf("%d\n",res);
  37. return ;
  38. }

代码在这里!

TARJAN:

缩点再建图,这是最主流的方法,不再赘述:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. bitset<> t[];
  4. int n,ans=,e[],in[];char s[];
  5. int dfn[],low[],st[],num=,top=,cnt=;
  6. bool ins[];
  7. int tot=,first[],len[];
  8. vector<int> edge[],scc[];
  9. struct node{int v,next;}eg[];
  10. inline void add(int x,int y)
  11. {
  12. eg[++tot].v=y;
  13. eg[tot].next=first[x];
  14. first[x]=tot;
  15. }
  16. inline void tarjan(int x)
  17. {
  18. dfn[x]=low[x]=++num;
  19. st[++top]=x;ins[x]=;
  20. for(register int i=;i<edge[x].size();i++)
  21. {
  22. int to=edge[x][i];
  23. if(!dfn[to])
  24. {
  25. tarjan(to);
  26. low[x]=min(low[x],low[to]);
  27. }
  28. else if(ins[to])
  29. low[x]=min(low[x],dfn[to]);
  30. }
  31. if(dfn[x]==low[x])
  32. {
  33. cnt++;int y;
  34. do{
  35. y=st[top--],ins[y]=;
  36. t[cnt][y]=,++len[cnt];
  37. e[y]=cnt,scc[cnt].push_back(y);
  38. }while(x!=y);
  39. }
  40. }
  41. int main()
  42. {
  43. scanf("%d",&n);
  44. for(register int i=;i<=n;i++)
  45. {
  46. scanf("%s",s+);
  47. for(register int j=;j<=n;j++)
  48. if(s[j]=='')edge[i].push_back(j);
  49. }
  50. for(register int i=;i<=n;i++)
  51. if(!dfn[i])tarjan(i);
  52. for(register int i=;i<=n;i++)
  53. for(register int j=;j<edge[i].size();j++)
  54. {
  55. int to=edge[i][j];
  56. if(e[i]==e[to])continue;
  57. add(e[i],e[to]);
  58. in[e[to]]++;
  59. }
  60. queue<int> q;
  61. for(register int i=;i<=cnt;i++)
  62. if(!in[i])q.push(i);
  63. while(!q.empty())
  64. {
  65. int x=q.front();q.pop();
  66. for(register int i=first[x];i;i=eg[i].next)
  67. {
  68. int to=eg[i].v;
  69. t[to]|=t[x];
  70. in[to]--;
  71. if(!in[to])q.push(to);
  72. }
  73. }
  74. for(register int i=;i<=n;i++)
  75. ans+=t[i].count()*len[i];
  76. printf("%d",ans);
  77. }

BITSET:

我们借助c++STL解决问题,对于每个点建一个bitset;

bitset可以理解为一个加长的二进制数,普通二进制数的位运算bitset都能做,(&|~^>><<)

C++的 bitset 在 bitset 头文件中,它是一种类似数组的结构,它的每一个元素只能是0或1,每个元素仅用1bit空间。

定义:bitset<n>b//长度为n,名称为b的一个bitset

相关函数:

b.size() 返回大小(位数)

b.count() 返回1的个数

b.any() 返回是否有1

b.none() 返回是否没有1

b.set() 全都变成1

b.set(p) 将第p + 1位变成1

b.set(p, x) 将第p + 1位变成x

b.reset() 全都变成0

b.reset(p) 将第p + 1位变成0

b.flip() 全都取反

b.flip(p) 将第p + 1位取反

b.to_ulong() 返回它转换为unsigned long的结果,如果超出范围则报错

b.to_ullong() 返回它转换为unsigned long long的结果,如果超出范围则报错

b.to_string() 返回它转换为string的结果

至于它的时间复杂度,和电脑本身有关,一般来说是使复杂度/32

一段关于bitset的代码:

  1. #include <cstdio>
  2. #include <bitset>
  3. #include <cstring>
  4. #include <iostream>
  5. #include <algorithm>
  6. const int N=10;
  7. std::bitset<N> a,b;//如果定义数组写成bitset<N> a[M];
  8. int main(){
  9. puts("stage 0");
  10. std::cout<<a<<std::endl;
  11. std::cout<<b<<std::endl;
  12. a[1]=1;
  13. b[0]=1;
  14. puts("stage 1");
  15. std::cout<<a[1]<<std::endl;
  16. std::cout<<b[0]<<std::endl;
  17. std::cout<<a<<std::endl;
  18. std::cout<<b<<std::endl;
  19. a=a|b;//等效于a|=b
  20. puts("stage 2");
  21. std::cout<<a<<std::endl;
  22. a=a<<3;//等效于a<<=3
  23. puts("stage 3");
  24. std::cout<<a<<std::endl;
  25. a=a>>3;//等效于a>>=3
  26. puts("stage 4");
  27. std::cout<<a<<std::endl;
  28. a=a^b;//等效于a^=b
  29. puts("stage 5");
  30. std::cout<<a<<std::endl;
  31. a=a&b;//等效于a&=b;
  32. puts("stage 6");
  33. std::cout<<a<<std::endl;
  34. a.set();
  35. puts("stage 7");
  36. std::cout<<a<<std::endl;
  37. a.reset();
  38. puts("stage 8");
  39. std::cout<<a<<std::endl;
  40. a=~b;
  41. puts("stage 9");
  42. std::cout<<a<<std::endl;
  43. std::cout<<b<<std::endl;
  44. a[2]=0,a[5]=0;
  45. puts("stage 10");
  46. std::cout<<a<<std::endl;
  47. std::cout<<a.count()<<" "<<a.size()<<std::endl;
  48. b=a;
  49. puts("stage 11");
  50. std::cout<<b<<std::endl;
  51. a=5;
  52. puts("stage 12");
  53. std::cout<<a<<std::endl;
  54. return 0;
  55. }
  56. //时间复杂度,整体操作都是长度/32(64位机器除64),单个操作(操作单个位)是O(1)的
  57. //空间复杂度,8位1字节,具体计算规则为在32位机器上Size = 4 * ((N + 31) / 32)在64位机器上Size = 8* ((N + 63) / 64)
  1. bitset<> foo ("");
  2.  
  3. string s = foo.to_string();  //将bitset转换成string类型
  4. unsigned long a = foo.to_ulong();  //将bitset转换成unsigned long类型
  5. unsigned long long b = foo.to_ullong();  //将bitset转换成unsigned long long类型
  6.  
  7. cout << s << endl;  //
  8. cout << a << endl;  //
  9. cout << b << endl;  //
  1. bitset<> foo ("");
  2.  
  3. cout << foo.flip() << endl;  //10011111  (flip函数传参数时,用于将参数位取反,本行代码将foo下标2处"反转",即0变1,1变0
  4. cout << foo.flip() << endl;   //01100000  (flip函数不指定参数时,将bitset每一位全部取反
  5.  
  6. cout << foo.set() << endl;    //11111111  (set函数不指定参数时,将bitset的每一位全部置为1
  7. cout << foo.set(,) << endl;  //11110111  (set函数指定两位参数时,将第一参数位的元素置为第二参数的值,本行对foo的操作相当于foo[3]=0
  8. cout << foo.set() << endl;   //11111111  (set函数只有一个参数时,将参数下标处置为1
  9.  
  10. cout << foo.reset() << endl;  //11101111  (reset函数传一个参数时将参数下标处置为0
  11. cout << foo.reset() << endl;   //00000000  (reset函数不传参数时将bitset的每一位全部置为0

以上三段代码均来自大佬博客和题解(%%)

了解了bitset,我们要用它来解题了

b[i]表示一种状态,若b[i][j]=1,则表示有一条由i向j连的边(i也要和自己连边)

若i能到j,那么i也能到j所能到的点,那么直接b[i]|b[j]即能把状态转移:

  1. for(int j=1;j<=n;j++)
  2. for(int i=1;i<=n;i++)
  3. if(bit[i][j]) bit[i]|=bit[j];

这里一定是要j在外层,i在内层,原因博主还没有想通,有理解的欢迎在评论区留言

最后我们用bitset函数中的count()统计每个bitset中1的个数,然后输出答案

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<algorithm>
  6. #include<queue>
  7. #include<vector>
  8. #include<bitset>
  9. #define MAXN 2005
  10. using namespace std;
  11. int n,ans=0;
  12. bitset<MAXN>bit[MAXN];
  13. char st[MAXN];
  14. int main(){
  15. scanf("%d",&n);
  16. for(int i=1;i<=n;i++){
  17. scanf("%s",st+1);
  18. for(int j=1;j<=n;j++)
  19. if(st[j]=='1')
  20. bit[i][j]=1;
  21. bit[i][i]=1;
  22. }
  23. for(int j=1;j<=n;j++)
  24. for(int i=1;i<=n;i++)
  25. if(bit[i][j]) bit[i]|=bit[j];
  26. for(int i=1;i<=n;i++)
  27. ans+=bit[i].count();
  28. printf("%d\n",ans);
  29. return 0;
  30. }

[JSOI2010]连通数 (dfs或tarjan或bitset)+bitset学习的更多相关文章

  1. 【BZOJ2208】[Jsoi2010]连通数 DFS

    [BZOJ2208][Jsoi2010]连通数 Description Input 输入数据第一行是图顶点的数量,一个正整数N. 接下来N行,每行N个字符.第i行第j列的1表示顶点i到j有边,0则表示 ...

  2. BZOJ 2208: [Jsoi2010]连通数( DFS )

    n只有2000,直接DFS就可以过了... -------------------------------------------------------------------------- #in ...

  3. BZOJ2208:[JSOI2010]连通数(DFS)

    Description Input 输入数据第一行是图顶点的数量,一个正整数N. 接下来N行,每行N个字符.第i行第j列的1表示顶点i到j有边,0则表示无边. Output 输出一行一个整数,表示该图 ...

  4. BZOJ 2208: [Jsoi2010]连通数 tarjan bitset

    2208: [Jsoi2010]连通数 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...

  5. bzoj2208 [Jsoi2010]连通数(scc+bitset)

    2208: [Jsoi2010]连通数 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1879  Solved: 778[Submit][Status ...

  6. BZOJ_2208_[Jsoi2010]连通数_强连通分量+拓扑排序+手写bitset

    BZOJ_2208_[Jsoi2010]连通数_强连通分量+拓扑排序+手写bitset Description Input 输入数据第一行是图顶点的数量,一个正整数N. 接下来N行,每行N个字符.第i ...

  7. 【BZOJ2208】[JSOI2010]连通数(Tarjan)

    [BZOJ2208][JSOI2010]连通数(Tarjan) 题面 BZOJ 洛谷 题解 先吐槽辣鸡洛谷数据,我写了个\(O(nm)\)的都过了. #include<iostream> ...

  8. bzoj2208:[Jsoi2010]连通数

    http://blog.csdn.net/u013598409/article/details/47037499 里面似乎有生成数据的... //我本来的想法是tarjan缩点之后然后将图遍历一遍就可 ...

  9. 2208: [Jsoi2010]连通数

    2208: [Jsoi2010]连通数 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1371  Solved: 557[Submit][Status ...

随机推荐

  1. Controller 获取前端数据

    默认支持的类型 在controller的方法的形参中直接定义上面这些类型的参数,springmvc会自动绑定. HttpServletRequest对象 HttpServletResponse对象 H ...

  2. [JZOJ 5698] 密码锁

    思路: 差分+排序 #include <bits/stdc++.h> using namespace std; #define ll long long const int maxn = ...

  3. (转)谈谈Android中的Rect类——奇葩的思维

    最近在工作中遇到了一些问题,总结下来就是Android中Rect这个类造成的.不得不说,不知道Android SDK的开发人员是怎么想的, 这个类设计的太奇葩了.首先介绍一下Rect类:Rect类主要 ...

  4. idea运行tomcat,控制台中文乱码

    加入参数:-Dfile.encoding=UTF-8

  5. ASP.NET的底层体系2

    文章引导 1.ASP.NET的底层体系1 2.ASP.NET的底层体系2 引言 接着上一篇ASP.NET的底层体系1我们继续往下走 一.System.Web.HttpRuntime.ProcessRe ...

  6. day23_1-re模块之转义字符、分组、方法

    #!/usr/bin/env python# -*- coding:utf-8 -*-# ------------------------------------------------------- ...

  7. git push到多个不同的远程仓库

    1.若现在本地有一个已经和github远程仓库关联好的本地仓库,平时都会将本地仓库push到github上. 2.有一天突然发现"码云"这个远程仓库网站,咦!这个还可以创建priv ...

  8. <scrapy爬虫>基本操作

    scrapy选择器的用法 //selector可以加可以不加 response.selector.xpath("//title/text()").extract_first() r ...

  9. 面试系列16 redis的持久化

    1.RDB和AOF两种持久化机制的介绍 RDB持久化机制,对redis中的数据执行周期性的持久化 AOF机制对每条写入命令作为日志,以append-only的模式写入一个日志文件中,在redis重启的 ...

  10. scrapy运行的整个流程

    Spiders: 负责处理所有的response,从这里面分析提取数据,获取Item字段所需要的数据,并将需要跟进的URL提交给引擎,再次进入到Scheduler调度器中 Engine: 框架的核心, ...