问题 A: 第一题
时间限制: 1 Sec 内存限制: 32 MB
提交: 169 解决: 25
题目描述
该题的目的是要你统计图的连通分支数。
输入
每个输入文件包含若干行,每行两个整数i,j,表示节点i和j之间存在一条边。
输出
输出每个图的联通分支数。
样例输入
1 4 4 3 5 5
样例输出
2
经验总结
这一题可以说是很坑了
首先题目没有说明结点编号的范围,事实上测试数据结点编号在1~1e6之间,标记数组稍微小一个数量级都会出现段错误
然后,就是要注意会出现自己指向自己的边,在使用邻接表进行记录时要区分一下,不然会有重复元素,但实际上不区分,也是可以通过的,就是耗时多一些
最后,就是如何访问顶点的问题了,我起初使用set集合记录所有顶点,但是由于数组很大,所以内存多了一点点,就超了,刚才又试了一下,5e5的数量级可以通过测试!!就是1e6数组大小小一半,这样用其他的结构(比如map用做标记,set用做结点)就不会超内存了,不过这里,还是建议直接在输入数据的时候就保存一个最大编号的变量,这样直接for循环就可以,省事而且高效。
这次也是学到了一些教训,比如,数组超内存限制使用其他结构,如果不在乎提交次数并且知道程序不通过的原因就是超出内存,就可以一半一半的减小,取试探测试数组的最大值,这样说不定可以留出一些空间使用其他的小型工具。
正确代码
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int maxn=500010;
vector<int> Adj[maxn];
bool flag[maxn]={false};
void DFS(int n)
{
flag[n]=true;
for(int i=0;i<Adj[n].size();++i)
{
int v=Adj[n][i];
if(flag[v]==false)
{
DFS(v);
}
}
}
int main()
{
int n,m,max=-1;
while(~scanf("%d",&n))
{
scanf("%d",&m);
if(m!=n)
{
Adj[n].push_back(m);
Adj[m].push_back(n);
}
else
{
Adj[n].push_back(m);
}
max=max>n?max:n;
max=max>m?max:m;
}
memset(flag,0,sizeof(flag));
int number=0;
for(int i=1;i<=max;++i)
{
if(flag[i]==false&&Adj[i].size()!=0)
{
number++;
DFS(i);
}
}
printf("%d\n",number);
return 0;
}