洛谷 P4306 [JSOI2010]连通数 - 图论、统计

洛谷 P4306 [JSOI2010]连通数

题目链接:洛谷 P4306 [JSOI2010]连通数

算法标签: 图论统计

题目

题目描述

度量一个有向图联通情况的一个指标是连通数,指图中可达顶点对个的个数。

如图

洛谷 P4306 [JSOI2010]连通数 p1

顶点 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 ++ ;
			}
		}
	}

}

之后按照操作统计答案即可,不过本题有毒瘤坑点。

本题坑点:

  1. 所读入矩阵之中没有空格,需要按照字符串读入在处理(被疯狂卡)
  2. 在输出的时候要记录\(ans + n\),原因在于这道题当中,自身与自身被算作连通,而在跑SPFA时候是没有记录自己的。

由于一个01串,假设为\(011111000001\) ,在这道题的题目下可以很简单的处理为\(0101\)这样一个串,在进行两种操作取最小值。

仔细分析这两种操作的时候,我们可以得到以下结论:

  • 我们可以将所有中操作找出两种最优的:

    1. 将0放在一边,1放在一边,最终一次转换
    2. 每一个都单独转换
  • 将字符串第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;
}
posted @ 2019-11-13 18:50  筱柒_Littleseven  阅读(156)  评论(0编辑  收藏  举报