洛谷 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章