洛谷P1347 排序

题目链接

看题

数据范围

        这道题给的数据范围很小,所以支持一通乱搞。

读题并且看样例

        发现题目给你了这些字母的对应关系,要你判断给出的关系是否能确定这些字母的顺序。很明显,有三种情况:1.能从第 x 个关系中确定关系;2.在第 x 个关系中发现矛盾点;3.给出的关系不能确定顺序。

分析

        要从这些关系之间找到字母顺序,顺序从小到大,要找顺序,那很容易就能想到拓扑排序,再看题目,发现这些关系可以转化为有向无环图中的关系,比如关系A<B,可以转化为图中A->B

拓扑排序的板子

vector<vector<int> > adj; // 邻接表
int in[N];  // 存储每个结点的入度

bool toposort() {

	vector<int> L;
	queue<int> S;
	//预处理:把入度为0的点都放进去
	for (int i = 1; i <= n; i++)
		if (!in[i]) 
			S.push(i);

	while (!S.empty()) {
		int u = S.front(); S.pop();
		L.push_back(u);
		for (auto v : adj[u])
			if (!--in[v]) // 将当前点所有的出边的终点入度-1    如果 入度 = 0 则入队
				S.push(v);
	}

	if (L.size() == n) { // 当所有点都遍历了
		for (auto i : L) cout << i << ' ';
		return true;
	}
	else 
		return false;
}
void solve() {

	cin >> n >> m; // n个点,m条边

	adj.resize(n + 1);

	int u, v;
    for(int i = 1; i <= m; i++){
		cin >> u >> v;
		adj[u].push_back(v);
		in[v]++;
	}
	
	toposort();

}

处理数据

预处理

        入度初始化为-1的原因在处理入度那有详细解释

adj.resize(n + 5);
memset(in, -1, sizeof in);

处理输入

        用三个字符变量分别存  字母(x) <小于符号(ch) 字母(y) (也可以用字符串读一行)

cin >> x >> ch >> y;
u = x - 'A' + 1;
v = y - 'A' + 1;

建图

        用邻接表存,u是有向边的起点,v是有向边的终点

adj[u].push_back(v);

处理入度

        因为是边输入关系边拓扑,有些关系的数据不能从一开始就全部输入进去,在拓扑排序的函数中(toposort()函数),会将所有入度为0的点都放进 待处理队列S 中,这样会造成存 顺序的数组L 的大小始终为n(点的个数)。但是初始化为-1,在输入这两个点的时候,再赋值为0,就可以避免这个问题。

if (in[u] == -1) in[u] = 0;
if (in[v] == -1) in[v] = 0;
in[v]++;

拓扑函数中

        另开一个用于 记录入度的数组inn 的原因: 拓扑排序里面会减掉数组中的点的入度,会对原有入度数据造成影响,并且是多次进行拓扑排序。然后用一个数组res记录确定顺序后的字母顺序(用数字记录)。
 

错误思路

        一开始想的是,数据范围很小,乱搞搞也能过, 并且要判断是否成环,就将并查集查环的板子给贴上去,然后处理一下数据,判断一下第二种情况就行了。但是测完样例之后交上去,10分,把第二种情况注释掉再交,30分,我就知道不能这样搞了。

调整思路(但不完全)

        要判断是否成环,我将每次拓扑中访问的点的个数记录下来,再和已经有了的点的个数进行比较,如果不相等,就说明成环了。用STL中的模板set来存储目前字母(点)个数(set可以去重),然后再每次拓扑排序中用一个计数器记录访问了的点的个数。

//每次把起点和终点加进去
point.insert(u);
point.insert(v);
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
int toposort() {

	cnt = 0;

	vector<int>L;
	queue<int>S;

	rep(i, 1, n) 
		inn[i] = in[i];

	rep(i, 1, n) {
		if (!inn[i]) {
			S.push(i);
			cnt++; // 计数器++
		}
	}

	while (!S.empty()) {

		int u = S.front(); S.pop();

		L.push_back(u);

		for (auto v : adj[u])
			if (!--inn[v]) {
				S.push(v);
				cnt++; // 计数器++
			}
	}

	if (L.size() == n) {
		res = L;
		return 1;
	}
	if (point.size() != cnt) 
		return 2;

	return 0;
}

加上判断之后交上去,发现80分,看了还有某种情况没处理对。

再调整思路

        发现,有这样一个情况,多个点的出度都为0的时候,是不能确定顺序的,所以加上一个out数组,用于记录出度,处理方式和入度数组in一样。拓扑中每次判断出度为0的点是否唯一即可。

int t = 0;
rep(i, 1, n)
	if (!out[i])
		t++;

        交上去,90,我#¥%……&

再再调整思路

        发现,我忽略了自成环的情况,即关系A<A这种情况。所以改变处理输入的方式。

将输入存下来,遇到自成环的情况直接输出矛盾即可。

rep(i, 1, m) {
	cin >> x >> ch >> y;

	if (x == y) {
		std::cout << "Inconsistency found after " << i << " relations.\n";
		return;
	}

	vp[i].first = x - 'A' + 1;
	vp[i].second = y - 'A' + 1;
}

        交上去,AC了。

总结

        这是一道绿题,比较考思维,处理输入,建图,处理入度,还有成环的判断,都需要考虑完全。

附上AC代码

#define _CRT_SECURE_NO_WARNINGS
#define itn int
#define PII pair<int, int>
#define PLI pair<long long, int>
#define fep(i,a,b) for(int i = (a); i >= (b); --i)
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#include<bits/stdc++.h>
using ll = long long;
using ldou = long double;
using unll = unsigned long long;
using namespace std;

//

unll n, m, k;
const int N = 1005;
const int dx[4] = { 1,-1, 0, 0 };
const int dy[4] = { 0, 0, 1,-1 };
//priority_queue<unll, vector<unll>, greater<unll> >pq;

vector<vector<int> >adj; // 邻接矩阵存图
int in[N]; // 入度
int out[N]; // 出度

vector<PII>vp; // 用pair存下题目给定的关系
vector<int>res; // 存成功确定顺序后的字母
set<int>point; // 用来存储目前字母(点)个数

int cnt; // 用于记录拓扑排序里面点的访问个数
int inn[N];// 另开一个用于记录入度的数组的原因: 拓扑排序里面会减掉数组中的点的入度

// 拓扑的板子 不同于板子的地方 用  //******  做标记
int toposort() { 

	cnt = 0;//******

	int t = 0;//******
	rep(i, 1, n)
		if (!out[i])
			t++; // 每一次出度为0的点的个数应该为1,这样才能严格确定顺序

	vector<int>L;
	queue<int>S;

	rep(i, 1, n) inn[i] = in[i];//******

	rep(i, 1, n) {
		if (!inn[i]) {
			S.push(i);
			cnt++;//******
		}
	}

	while (!S.empty()) {

		int u = S.front(); S.pop();

		L.push_back(u);

		for (auto v : adj[u])
			if (!--inn[v]) {
				S.push(v);
				cnt++;//******
			}
	}

	if (L.size() == n && t == 1) {//******
		res = L;//******
		/*for (auto u : L) cout << u << ' ';
		cout << '\n';*/
		return 1;
	}
	if (point.size() != cnt) return 2;//******

	//point.clear();
	return 0;
}

void solve() {

	cin >> n >> m;

	adj.resize(n + 5);
	vp.resize(m + 5);
	memset(in, -1, sizeof in);
	memset(out, -1, sizeof out);

	char x, y, ch;
	int u, v;

	rep(i, 1, m) {
		cin >> x >> ch >> y;

		if (x == y) {
			std::cout << "Inconsistency found after " << i << " relations.\n";
			return;
		}

		vp[i].first = x - 'A' + 1;
		vp[i].second = y - 'A' + 1;
	}

	rep(i, 1, m) {

		u = vp[i].first;
		v = vp[i].second;

		point.insert(u);
		point.insert(v);

		adj[u].push_back(v);

		if (in[u] == -1) in[u] = 0;
		if (in[v] == -1) in[v] = 0;
		in[v]++;

		if (out[u] == -1) out[u] = 0;
		if (out[v] == -1) out[v] = 0;
		out[u]++;

		if (toposort() == 1) {
			std::cout << "Sorted sequence determined after " << i << " relations: ";
			for (int j = 0; j < res.size(); j++)
				std::cout << char(res[j] - 1 + 'A');
			std::cout << ".\n";
			return;
		}
		else if (toposort() == 2) {
			std::cout << "Inconsistency found after " << i << " relations.\n";
			return;
		}
	}

	std::cout << "Sorted sequence cannot be determined.\n";

}

signed main() {
	//std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	//freopen("wrt.txt", "r", stdin); //freopen("out.txt", "w", stdout);
	int TTT = 1; //cin >> TTT;
	while (TTT--) {
		solve();
	}
	/*while (cin >> n >> m) {
		solve();
	}*/

	return 0;
}

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值