AcWing_367

链接

  • 点此跳转

思路和代码

设缩点后,起点有 P P P 个,终点有 Q Q Q 个。

第一问,只要统计入度为 0 0 0 的点的数量,即 P P P

第二问:

  • S C C _ C N T = 1 SCC\_CNT=1 SCC_CNT=1 ,那么答案为 0 0 0
  • 否则,答案为 m a x ( P , Q ) max(P, Q) max(P,Q)

简单思路:

  • P ≤ Q P\le Q PQ

  • P = 1 P=1 P=1 时,只需要让所有终点连向起点,答案为 Q Q Q

  • P > 1 P > 1 P>1 时,任选出两组起点和终点 p 1 , q 1 , p 2 , q 2 p_1,q_1,p_2,q_2 p1,q1,p2,q2 , 只需让 q 1 q_1 q1 连向 p 2 p_2 p2 ,那么 P = P − 1 , Q = Q − 1 P=P-1,Q=Q-1 P=P1,Q=Q1 ,依次这样操作直至 P = 1 P=1 P=1 ,化为第一种情况。

  • 第二种情况答案为 Q − ( P − 1 ) + ( P − 1 ) = Q Q-(P-1)+(P-1) = Q Q(P1)+(P1)=Q

  • 还须特判一下,当只有一个强连通分量时,答案为 0 0 0

  • 综上,第二问答案为 scc_cnt > 1 ? max(P, Q) : 0

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define endl '\n'
#define PI acos(-1)
#define LL long long
#define INF 0x3f3f3f3f
#define lowbit(x) (-x&x)
#define PII pair<int, int>
#define PIL pair<int, long long>
#define mem(a, b) memset(a, b, sizeof a)
#define rev(x) reverse(x.begin(), x.end())
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)

using namespace std;

const int N = 110, M = 10010;

int n;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp;
int din[N], dout[N];
int id[N], scc_cnt;
bool in_stk[N];
int stk[N], top;

void add(int a, int b) { // 加边函数
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void tarjan(int u) {  //tarjan算法求SCC
	dfn[u] = low[u] = ++ timestamp;
	stk[ ++ top] = u, in_stk[u] = true;
	for (int i = h[u]; ~i; i = ne[i]) {
		int j = e[i];
		if (!dfn[j]) {
			tarjan(j);
			low[u] = min(low[u], low[j]);
		} else if (in_stk[j]) low[u] = min(low[u], dfn[j]);
	}
	
	if (low[u] == dfn[u]) {
		int y;
		++ scc_cnt;
		do {
			y = stk[top -- ];
			in_stk[y] = false;
			id[y] = scc_cnt;
		} while (y != u);
	}
}

void solve() {
	// 设缩完点后有P个起点,Q个终点
	// 第一问答案为P
	// 如果强连通分量数量大于1,第二问答案为max(P, Q)
	// 否则为0
	
	// 初始化
	mem(h, -1);
	
	// 建图,加边
	cin >> n;
	for (int i = 1; i <= n; i ++ ) {
		int t;
		while (cin >> t, t) add(i, t);
	}
	
	// 求SCC
	for (int i = 1; i <= n; i ++ ) if (!dfn[i]) tarjan(i);
	
	// 求每个强连通分量的出入度
	for (int i = 1; i <= n; i ++ ) {
		for (int j = h[i]; ~j; j = ne[j]) {
			int k = e[j];
			int a = id[i], b = id[k];
			if (a != b) {  //a 和 b 不在同一强连通分量中
				dout[a] ++ ;
				din[b] ++ ;
			}
		}
	}
		
	// 求P和Q
	int P = 0, Q = 0;
	for (int i = 1; i <= scc_cnt; i ++ ) {
		if (!din[i]) P ++ ;
		if (!dout[i]) Q ++ ;
	}
	
	//输出结论
	cout << P << endl;
	cout << (scc_cnt > 1 ? max(P, Q) : 0) << endl;
}

int main() {
	IOS;
	
	solve();
	
	return 0;
}

你可能感兴趣的:(图论,算法,图论,c++)