试题编号: | 201509-4 |
试题名称: | 高速公路 |
时间限制: | 1.0s |
内存限制: | 256.0MB |
问题描述: | 问题描述 某国有n个城市,为了使得城市间的交通更便利,该国国王打算在城市之间修一些高速公路,由于经费限制,国王打算第一阶段先在部分城市之间修一些单向的高速公路。 输入格式 输入的第一行包含两个整数n, m,分别表示城市和单向高速公路的数量。 输出格式 输出一行,包含一个整数,表示便利城市对的数量。 样例输入 5 5 样例输出 3 样例说明
评测用例规模与约定 前30%的评测用例满足1 ≤ n ≤ 100, 1 ≤ m ≤ 1000; |
终于写完这道题啦~
我之前一直就觉得这道题好可怕。用了一个DFS之前得了60,然后仔细一下,这不就是求连通分支嘛!
每个连通分支内要结对,就用一个公式C(2,n),就可以了,即n*(n-1)/2
然后去网上查了一下连通分支的算法,看到了tarjan算法,下意思读成太监了哈哈哈。
然后就成功了。
#include <iostream>
#include <bits/stdc++.h>
#define maxn 10005
using namespace std;
vector<int>V[maxn];//点
int low[maxn];
int dfn[maxn];
bool instack[maxn];//是否在栈中
int Time;//时间戳
int answer = 0;
int Stack[maxn];
int top;//栈顶
void tarjan(int i)
{
int j;
int k;
dfn[i] = low[i] = ++Time;
instack[i] = 1;
Stack[++top] = i;
for(j = 0; j<V[i].size(); j++)
{
k = V[i][j];
if(!dfn[k])//没有进栈
{
tarjan(k);
low[i] = min(low[i],low[k]);
}
else if(instack[k])
{
low[i] = min(low[i],dfn[k]);
}
}
if(dfn[i] == low[i])
{
int sum = 0;
do
{
j = Stack[top--];
instack[j] = 0;
sum++;
}
while(i!=j);
answer+=(sum*(sum-1))/2;
}
}
void solve(int n)
{
int i;
for(i = 1; i<=n; i++)
{
if(!dfn[i])
tarjan(i);
}
}
int main()
{
int n,m;
int a,b;
int i;
memset(low,sizeof(low),0);
memset(dfn,sizeof(dfn),0);
memset(instack,sizeof(instack),0);
memset(Stack,sizeof(Stack),0);
cin>>n>>m;
for(i = 0; i<m; i++)
{
cin>>a>>b;
V[a].push_back(b);
}
solve(n);
cout<<answer;
return 0;
}
网上有很多tarjan的解释博客,最让我困惑的是:
一个是low[k],一个是dfn[k]??
为什么?