[JSOI2010]连通数 (dfs或tarjan或bitset)+bitset学习
题目描述
输入格式
输入数据第一行是图顶点的数量,一个正整数N。 接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。
输出格式
输出一行一个整数,表示该图的连通数。
样例
样例输入
3
010
001
100
样例输出
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存的是边的信息,所以不用考虑环。
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<cmath>
- #include<algorithm>
- #include<queue>
- #include<vector>
- #define MAXN 2005
- using namespace std;
- int n,ans[MAXN],res=;
- char ch[MAXN][MAXN];
- vector<int>mapa[MAXN];
- bool vis[MAXN][MAXN];//vis[i][j]表示是否访问过由i向j的边
- void dfs(int st,int now){//st:起点,now:当前节点
- ans[st]++;
- vis[st][now]=;
- int m=mapa[now].size();
- for(int i=;i<m;i++){
- if(!vis[st][mapa[now][i]])
- dfs(st,mapa[now][i]);
- }
- }
- int main(){
- scanf("%d",&n);
- for(int i=;i<=n;i++){
- scanf("%s",ch[i]+);
- for(int j=;j<=n;j++){
- if(ch[i][j]=='')
- mapa[i].push_back(j);
- //cout<<ch[i][j];
- }
- //cout<<endl;
- }
- for(int i=;i<=n;i++)
- dfs(i,i),res+=ans[i];
- printf("%d\n",res);
- return ;
- }
代码在这里!
TARJAN:
缩点再建图,这是最主流的方法,不再赘述:
- #include<bits/stdc++.h>
- using namespace std;
- bitset<> t[];
- int n,ans=,e[],in[];char s[];
- int dfn[],low[],st[],num=,top=,cnt=;
- bool ins[];
- int tot=,first[],len[];
- vector<int> edge[],scc[];
- struct node{int v,next;}eg[];
- inline void add(int x,int y)
- {
- eg[++tot].v=y;
- eg[tot].next=first[x];
- first[x]=tot;
- }
- inline void tarjan(int x)
- {
- dfn[x]=low[x]=++num;
- st[++top]=x;ins[x]=;
- for(register int i=;i<edge[x].size();i++)
- {
- int to=edge[x][i];
- if(!dfn[to])
- {
- tarjan(to);
- low[x]=min(low[x],low[to]);
- }
- else if(ins[to])
- low[x]=min(low[x],dfn[to]);
- }
- if(dfn[x]==low[x])
- {
- cnt++;int y;
- do{
- y=st[top--],ins[y]=;
- t[cnt][y]=,++len[cnt];
- e[y]=cnt,scc[cnt].push_back(y);
- }while(x!=y);
- }
- }
- int main()
- {
- scanf("%d",&n);
- for(register int i=;i<=n;i++)
- {
- scanf("%s",s+);
- for(register int j=;j<=n;j++)
- if(s[j]=='')edge[i].push_back(j);
- }
- for(register int i=;i<=n;i++)
- if(!dfn[i])tarjan(i);
- for(register int i=;i<=n;i++)
- for(register int j=;j<edge[i].size();j++)
- {
- int to=edge[i][j];
- if(e[i]==e[to])continue;
- add(e[i],e[to]);
- in[e[to]]++;
- }
- queue<int> q;
- for(register int i=;i<=cnt;i++)
- if(!in[i])q.push(i);
- while(!q.empty())
- {
- int x=q.front();q.pop();
- for(register int i=first[x];i;i=eg[i].next)
- {
- int to=eg[i].v;
- t[to]|=t[x];
- in[to]--;
- if(!in[to])q.push(to);
- }
- }
- for(register int i=;i<=n;i++)
- ans+=t[i].count()*len[i];
- printf("%d",ans);
- }
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的代码:
- #include <cstdio>
- #include <bitset>
- #include <cstring>
- #include <iostream>
- #include <algorithm>
- const int N=10;
- std::bitset<N> a,b;//如果定义数组写成bitset<N> a[M];
- int main(){
- puts("stage 0");
- std::cout<<a<<std::endl;
- std::cout<<b<<std::endl;
- a[1]=1;
- b[0]=1;
- puts("stage 1");
- std::cout<<a[1]<<std::endl;
- std::cout<<b[0]<<std::endl;
- std::cout<<a<<std::endl;
- std::cout<<b<<std::endl;
- a=a|b;//等效于a|=b
- puts("stage 2");
- std::cout<<a<<std::endl;
- a=a<<3;//等效于a<<=3
- puts("stage 3");
- std::cout<<a<<std::endl;
- a=a>>3;//等效于a>>=3
- puts("stage 4");
- std::cout<<a<<std::endl;
- a=a^b;//等效于a^=b
- puts("stage 5");
- std::cout<<a<<std::endl;
- a=a&b;//等效于a&=b;
- puts("stage 6");
- std::cout<<a<<std::endl;
- a.set();
- puts("stage 7");
- std::cout<<a<<std::endl;
- a.reset();
- puts("stage 8");
- std::cout<<a<<std::endl;
- a=~b;
- puts("stage 9");
- std::cout<<a<<std::endl;
- std::cout<<b<<std::endl;
- a[2]=0,a[5]=0;
- puts("stage 10");
- std::cout<<a<<std::endl;
- std::cout<<a.count()<<" "<<a.size()<<std::endl;
- b=a;
- puts("stage 11");
- std::cout<<b<<std::endl;
- a=5;
- puts("stage 12");
- std::cout<<a<<std::endl;
- return 0;
- }
- //时间复杂度,整体操作都是长度/32(64位机器除64),单个操作(操作单个位)是O(1)的
- //空间复杂度,8位1字节,具体计算规则为在32位机器上Size = 4 * ((N + 31) / 32)在64位机器上Size = 8* ((N + 63) / 64)
- bitset<> foo ("");
- string s = foo.to_string(); //将bitset转换成string类型
- unsigned long a = foo.to_ulong(); //将bitset转换成unsigned long类型
- unsigned long long b = foo.to_ullong(); //将bitset转换成unsigned long long类型
- cout << s << endl; //
- cout << a << endl; //
- cout << b << endl; //
- bitset<> foo ("");
- cout << foo.flip() << endl; //10011111 (flip函数传参数时,用于将参数位取反,本行代码将foo下标2处"反转",即0变1,1变0
- cout << foo.flip() << endl; //01100000 (flip函数不指定参数时,将bitset每一位全部取反
- cout << foo.set() << endl; //11111111 (set函数不指定参数时,将bitset的每一位全部置为1
- cout << foo.set(,) << endl; //11110111 (set函数指定两位参数时,将第一参数位的元素置为第二参数的值,本行对foo的操作相当于foo[3]=0
- cout << foo.set() << endl; //11111111 (set函数只有一个参数时,将参数下标处置为1
- cout << foo.reset() << endl; //11101111 (reset函数传一个参数时将参数下标处置为0
- 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]即能把状态转移:
- for(int j=1;j<=n;j++)
- for(int i=1;i<=n;i++)
- if(bit[i][j]) bit[i]|=bit[j];
这里一定是要j在外层,i在内层,原因博主还没有想通,有理解的欢迎在评论区留言
最后我们用bitset函数中的count()统计每个bitset中1的个数,然后输出答案
代码:
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<cmath>
- #include<algorithm>
- #include<queue>
- #include<vector>
- #include<bitset>
- #define MAXN 2005
- using namespace std;
- int n,ans=0;
- bitset<MAXN>bit[MAXN];
- char st[MAXN];
- int main(){
- scanf("%d",&n);
- for(int i=1;i<=n;i++){
- scanf("%s",st+1);
- for(int j=1;j<=n;j++)
- if(st[j]=='1')
- bit[i][j]=1;
- bit[i][i]=1;
- }
- for(int j=1;j<=n;j++)
- for(int i=1;i<=n;i++)
- if(bit[i][j]) bit[i]|=bit[j];
- for(int i=1;i<=n;i++)
- ans+=bit[i].count();
- printf("%d\n",ans);
- return 0;
- }
[JSOI2010]连通数 (dfs或tarjan或bitset)+bitset学习的更多相关文章
- 【BZOJ2208】[Jsoi2010]连通数 DFS
[BZOJ2208][Jsoi2010]连通数 Description Input 输入数据第一行是图顶点的数量,一个正整数N. 接下来N行,每行N个字符.第i行第j列的1表示顶点i到j有边,0则表示 ...
- BZOJ 2208: [Jsoi2010]连通数( DFS )
n只有2000,直接DFS就可以过了... -------------------------------------------------------------------------- #in ...
- BZOJ2208:[JSOI2010]连通数(DFS)
Description Input 输入数据第一行是图顶点的数量,一个正整数N. 接下来N行,每行N个字符.第i行第j列的1表示顶点i到j有边,0则表示无边. Output 输出一行一个整数,表示该图 ...
- BZOJ 2208: [Jsoi2010]连通数 tarjan bitset
2208: [Jsoi2010]连通数 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...
- bzoj2208 [Jsoi2010]连通数(scc+bitset)
2208: [Jsoi2010]连通数 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1879 Solved: 778[Submit][Status ...
- BZOJ_2208_[Jsoi2010]连通数_强连通分量+拓扑排序+手写bitset
BZOJ_2208_[Jsoi2010]连通数_强连通分量+拓扑排序+手写bitset Description Input 输入数据第一行是图顶点的数量,一个正整数N. 接下来N行,每行N个字符.第i ...
- 【BZOJ2208】[JSOI2010]连通数(Tarjan)
[BZOJ2208][JSOI2010]连通数(Tarjan) 题面 BZOJ 洛谷 题解 先吐槽辣鸡洛谷数据,我写了个\(O(nm)\)的都过了. #include<iostream> ...
- bzoj2208:[Jsoi2010]连通数
http://blog.csdn.net/u013598409/article/details/47037499 里面似乎有生成数据的... //我本来的想法是tarjan缩点之后然后将图遍历一遍就可 ...
- 2208: [Jsoi2010]连通数
2208: [Jsoi2010]连通数 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1371 Solved: 557[Submit][Status ...
随机推荐
- Controller 获取前端数据
默认支持的类型 在controller的方法的形参中直接定义上面这些类型的参数,springmvc会自动绑定. HttpServletRequest对象 HttpServletResponse对象 H ...
- [JZOJ 5698] 密码锁
思路: 差分+排序 #include <bits/stdc++.h> using namespace std; #define ll long long const int maxn = ...
- (转)谈谈Android中的Rect类——奇葩的思维
最近在工作中遇到了一些问题,总结下来就是Android中Rect这个类造成的.不得不说,不知道Android SDK的开发人员是怎么想的, 这个类设计的太奇葩了.首先介绍一下Rect类:Rect类主要 ...
- idea运行tomcat,控制台中文乱码
加入参数:-Dfile.encoding=UTF-8
- ASP.NET的底层体系2
文章引导 1.ASP.NET的底层体系1 2.ASP.NET的底层体系2 引言 接着上一篇ASP.NET的底层体系1我们继续往下走 一.System.Web.HttpRuntime.ProcessRe ...
- day23_1-re模块之转义字符、分组、方法
#!/usr/bin/env python# -*- coding:utf-8 -*-# ------------------------------------------------------- ...
- git push到多个不同的远程仓库
1.若现在本地有一个已经和github远程仓库关联好的本地仓库,平时都会将本地仓库push到github上. 2.有一天突然发现"码云"这个远程仓库网站,咦!这个还可以创建priv ...
- <scrapy爬虫>基本操作
scrapy选择器的用法 //selector可以加可以不加 response.selector.xpath("//title/text()").extract_first() r ...
- 面试系列16 redis的持久化
1.RDB和AOF两种持久化机制的介绍 RDB持久化机制,对redis中的数据执行周期性的持久化 AOF机制对每条写入命令作为日志,以append-only的模式写入一个日志文件中,在redis重启的 ...
- scrapy运行的整个流程
Spiders: 负责处理所有的response,从这里面分析提取数据,获取Item字段所需要的数据,并将需要跟进的URL提交给引擎,再次进入到Scheduler调度器中 Engine: 框架的核心, ...