寒假2024/2/17

想开学了

强连通分量模板

#include <bits/stdc++.h>
using namespace std;
	
const int N = 1e5 + 5;

vector<int>e[N];
int dfn[N], low[N], tot;
int stk[N], instk[N], top;	// 栈的作用是记录正在访问的点
int scc[N], siz[N], cnt;	//最后的结果是scc[i] = k 记录第i个节点在第k个强连通分量里,大小是siz【k】

void tarjan(int x) {
	//入x时, 盖戳, 入栈
	dfn[x] = low[x] = ++tot;
	stk[++top] = x, instk[x] = 1;
	for (int y : e[x]) {
		if(!dfn[y]) {		//若y尚未访问
			tarjan(y);
			low[x] = min(low[x], low[y]);		//回x时更新low
		}
		else if(instk[y]) { //若y已访问且在栈中
			low[x] = min(low[x], low[y]);
		}
		// 剩下已经访问并且不在栈中的情况是y已经被访问完,不在枚举
	}
	//离x时,记录scc
	if(dfn[x] == low[x]) { //若x是scc的根
		int y; ++cnt;
		do{		//陆续把强连通分量出栈
			y = stk[top--]; instk[y] = 0;
			scc[y] = cnt; //scc编号
			++siz[cnt];   //scc大小
		} while(y != x);
	}
} 

uva297

题意:

给四分图,32 x 32,图是递归形式给出的,要求求出两个四分图合并后的新四分图的黑色块数。

思路:

起初我没弄懂给的样例448 640那些数字是怎么来的,后来才发现题目给的一块是好几个色素块(我是本币)

弄懂题意我们就可以想思路了,起初我看题目引导我们建树,我开始建立四叉树,但是越写越别扭,又要建树,又要合并。

所以我换了一种思路:直接填图,填两遍图,最后统计数量就行了。

(1)起初我们先给图赋值0表示全白。

(2)然后我们按顺序对两个字符串进行操作,如果全白不管,黑就涂黑,灰色就递归再涂。

但是我们要想怎么用递归跳转位置填图呢?

我看了dalao写的题解,很妙x, y, w三个参数位置和边长就能知道目标正方形了。

(3)最后我们统计数量乘以16就是答案。

最后有一点小bug,题目给的正方形是缩放的,样例给的是32 x 32的

#include <bits/stdc++.h>
using namespace std;

#define int long long
int arr[35][35] = {0};
int loc = -1;
string s;

void draw(int x, int y, int w) {	
	++loc;
	// cerr << x << " " << y << " " << w << " " << s[loc] << endl;
	if(s[loc] == 'f') {
		for (int i = x; i < x + w; i++) {
			for (int j = y; j > y - w; j--) {
				arr[i][j] = 1;
			}
		}
		return;
	}
	else if(s[loc] == 'p') {
		draw(x, y, w / 2);
		draw(x, y - w / 2, w / 2);
		draw(x + w / 2, y - w / 2, w / 2);
		draw(x + w / 2, y, w / 2);
	}
}

void print() {
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			cout << arr[i][j] << " ";
		}
		cout << endl;
	}
}

void solve() {	
	memset(arr, 0, sizeof(arr));

	for (int i = 0; i < 2; i++) {
		cin >> s;
		loc = -1;
		draw(0, 31, 32);
		// print();
	}

	int ans = 0;
	for (int i = 0; i < 32; i++) {
		for (int j = 0; j < 32; j++) {
			if(arr[i][j]) {
				ans++;
			}
		}
	}

	cout << "There are " << ans << " black pixels.\n";
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	int T = 1;
	cin >> T;
	while(T--) {
		solve();
	}

	return 0;
}

uva247

题意:

给n个人,m条通话记录,如果一群人能互相打给对方,就是一个团体,把每个团体输出。

思路:

这个题实质就是强连通分量,要加一点字符串的哈希。

刚学的,套模板,嘎嘎快。

#include <bits/stdc++.h>
using namespace std;
	
const int N = 100 + 5;

vector<int>e[N];
int dfn[N], low[N], tot;
int stk[N], instk[N], top;	// 栈的作用是记录正在访问的点
int scc[N], siz[N], cnt;	//最后的结果是scc[i] = k 记录第i个节点在第k个强连通分量里,大小是siz【k】
vector<string> name;
map<string, int>mp;

void tarjan(int x) {
	//入x时, 盖戳, 入栈
	dfn[x] = low[x] = ++tot;
	stk[++top] = x, instk[x] = 1;
	for (int y : e[x]) {
		if(!dfn[y]) {		//若y尚未访问
			tarjan(y);
			low[x] = min(low[x], low[y]);		//回x时更新low
		}
		else if(instk[y]) { //若y已访问且在栈中
			low[x] = min(low[x], low[y]);
		}
		// 剩下已经访问并且不在栈中的情况是y已经被访问完,不在枚举
	}
	//离x时,记录scc
	if(dfn[x] == low[x]) { //若x是scc的根
		int y; ++cnt;
		bool flag = 0;
		do{		//陆续把强连通分量出栈
			y = stk[top--]; instk[y] = 0;
			scc[y] = cnt; //scc编号
			if(flag) {
				cout << ", ";
			}
			else {
				flag = 1;
			}
			cout << name[y - 1];
			++siz[cnt];   //scc大小
		} while(y != x);
		cout << endl;
	}
} 

void solve(int n, int m) {
	tot = 0;
	top = 0;
	cnt = 0;
	for (int i = 1; i <= n; i++) {
		dfn[i] = 0;
		low[i] = 0;
		stk[i] = 0;
		instk[i] = 0;
		e[i].clear();
	}
	name.clear();
	mp.clear();
	for (int i = 0; i < m; i++) {
		string s, t;
		cin >> s >> t;
		if(mp.count(s) == 0) {
			name.push_back(s);
			mp[s] = name.size();
		}
		if(mp.count(t) == 0) {
			name.push_back(t);
			mp[t] = name.size();
		}
		int a = mp[s], b = mp[t];
		e[a].push_back(b);
	}
	for (int i = 1; i <= n; i++) {
		if(!dfn[i]) {
			tarjan(i);
		}
	}
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	int n, m;
	int cnt = 1;
	while(cin >> n >> m && n) {
		cout << "Calling circles for data set " << cnt++ << ":" << endl;
		solve(n, m);
	}

	return 0;
}
posted @ 2024-02-17 23:58  contiguous  阅读(14)  评论(0编辑  收藏  举报