洛谷 P4306 [JSOI2010]连通数 - 图论、统计
作者:互联网
洛谷 P4306 [JSOI2010]连通数
算法标签: 图论
,统计
题目
题目描述
度量一个有向图联通情况的一个指标是连通数,指图中可达顶点对个的个数。
如图
顶点 1 可达 1, 2, 3, 4, 5
顶点 2 可达 2, 3, 4, 5
顶点 3 可达 3, 4, 5
顶点 4, 5 都只能到达自身。
所以这张图的连通数为 14。
给定一张图,请你求出它的连通数
输入格式
输入数据第一行是图顶点的数量,一个正整数N。 接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。
输出格式
输出一行一个整数,表示该图的连通数。
输入输出样例
输入 #1
3
010
001
100
输出 #1
9
说明/提示
对于100%的数据,N不超过2000。
题解:
某机房巨佬JZYshuraK_彧 推荐的题,貌似说有很多做法,不过作为一个蒟蒻,我搬出了许久没有用到的 毒瘤 SPFA,思路是将SPFA中的松弛更新最短路改为连通计数,最终统计\(ans\)即可。
大致实现如下(链式前向星存图):
void spfa(int s)
{
memset(vis, 0, sizeof vis);
queue <int> q;
q.push(s);
vis[s] = 1;
while(!q.empty())
{
int x, y;
x = q.front();
q.pop();
for (int i = head[x]; i; i = nex[i])
{
y = to[i];
if (!vis[y])
{
q.push(y);
vis[y] = 1;
ans ++ ;
}
}
}
}
之后按照操作统计答案即可,不过本题有毒瘤坑点。
本题坑点:
- 所读入矩阵之中没有空格,需要按照字符串读入在处理(被疯狂卡)
- 在输出的时候要记录\(ans + n\),原因在于这道题当中,自身与自身被算作连通,而在跑SPFA时候是没有记录自己的。
由于一个01串,假设为\(011111000001\) ,在这道题的题目下可以很简单的处理为\(0101\)这样一个串,在进行两种操作取最小值。
仔细分析这两种操作的时候,我们可以得到以下结论:
我们可以将所有中操作找出两种最优的:
- 将0放在一边,1放在一边,最终一次转换
- 每一个都单独转换
将字符串第0位设为一个1,这样我们只需要统计由某一位为0而前一位为1的个数,这样的答案就是操作的总次数(可以推得无论如何进行操作总次数都一致)。而且我们可以发现无论如何进行操作,最后一次都是转换,所以我们可以得出
\(ans = (cnt - 1) * min(x, y) + y;\)
由此此题得解,注意本题数据需要开long long
。
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2020;
const int M = N * N;
int n, ans, vis[N];
int tot, to[M], nex[M], head[N];
void add(int x, int y)
{
to[++tot] = y;
nex[tot] = head[x];
head[x] = tot;
}
void spfa(int s)
{
memset(vis, 0, sizeof vis);
queue <int> q;
q.push(s);
vis[s] = 1;
while(!q.empty())
{
int x, y;
x = q.front();
q.pop();
for (int i = head[x]; i; i = nex[i])
{
y = to[i];
if (!vis[y])
{
q.push(y);
vis[y] = 1;
ans ++ ;
}
}
}
}
int main()
{
scanf("%d", &n);
char ch[2020];
for (int i = 1; i <= n; i ++ )
{
scanf("%s", ch + 1);
for (int j = 1; j <= n; j ++ )
{
if (ch[j] == '1')
add(i, j);
}
}
for (int i = 1; i <= n; i ++ )
spfa(i);
printf("%d", ans + n);
return 0;
}
标签:JSOI2010,head,洛谷,int,vis,连通数,ans,顶点 来源: https://www.cnblogs.com/littleseven777/p/11851759.html