2021 - 辽宁省第二届大学生程序设计竞赛

重现赛地址: 牛客

总共十三道题,A了七道题,罚时是同题数的榜首,惨痛银牌,再多一道就有金
了,悔しい,来年冲冲冲

剩下五道题不一定有题解(菜的不行),先从简单的开始:

M题:比赛!

在这里插入图片描述
题意:

首先输入一个 n ,之后 n 行给出 n 条信息,每条信息格式固定。如图,三个字符加一个用来分隔的 “ : ” ,每种字符代表一位选手,意思是第一位选手成绩排在第二位和第三位之间。根据选手给出的信息,将选手根据成绩进行一个可能的排序,能排出任意一种可能就输出序列,如果无论如何都不可能就输出No Answer。

当初看题看到关系网第一时间就往食物链方向卡了…然后又转链表map暴力,怎么都做不到完美记录下可能的输出状态。结果这是到图论拓扑题,属于是 开眼界 太菜了 (但凡当时刷点图论也不至此)

#include<bits/stdc++.h>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define ios_sync (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define ul u << 1
#define ur u << 1 | 1
#include<vector>
#include<string>
using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

//最根本的操作:对于每一条信息给的三个选手,建一条有向链
//比如对 A :BC ,建 B -> A -> C
//这样就记录下来了三个选手之间的关系

//同时每建一条边都去进行一个判定,比如 B -> A ,就去判定一下从 A 开始能不能走到 B 点,如果能走到就相当于存在一个环,代表给出的信息肯定是有矛盾的,这样最后肯定找不出一条序列满足所有信息

//如果信息全部都没有矛盾,相当于这个图里没有环的存在
//一个没有环的图,非常理所当然地就应该想到拓扑序列
// 
//B -> A ,A 的入度就该++,意味着 A 入队前应该先处理掉之前对其有影响的点(B),当它处理完影响自己的点后,此时输出肯定能满足所有采访(限制条件信息)

//素晴らしい

const int N = 505, M = 1005;
ll INF = 0x3f3f3f3f, mod = 1e9;
int n, m, k, T;
int h[N], ne[M], e[M], idx;//用一个邻接表,可能会有疑问重边该怎么处理?事实上重边也不影响,入度照常++就行,拓扑遍历某个点必定会把连向它的所有点(即使重复相连,这条边也肯定记录在表中)的入度--,所以不会有影响
int dist[N], ru[N], ss[N];
//ss记录离散后的数字对应的字符,毕竟最后还要输出
bool st[N];
map<char, int> mp;//离散,把选手编号转化成数字
char str[10];

void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
	ru[b]++;//建边,单向,入度++
}

bool dfs(int a, int b) {
	//判断有没有边 a -> b
	mem(st, 0);
	queue<int> q;

	q.push(a);
	st[a] = true;//从该点开始往下跑一遍图,标记点防止跑重边

	while (q.size())
	{
		int t = q.front();
		q.pop();

		for (int i = h[t]; ~i; i = ne[i]) {
			int j = e[i];
			if (!st[j]) {
				if (j == b)return true;//找的到 b

				st[j] = true;
				q.push(j);
			}
		}
	}
	return false;//找不到 b
}

void top_sort() {
	queue<int> q;

	for (int i = 1; i <= k; i++)
		if (!ru[i])q.push(i);//入度0点先入队

	while (q.size())
	{
		int t = q.front();
		q.pop();

		pri("%c", ss[t]);//随时输出拓扑序

		for (int i = h[t]; ~i; i = ne[i]) {
			int j = e[i];
			ru[j]--;
			if (!ru[j])
				q.push(j);
		}
	}
}

int main() {
	
	sca("%d", &n);
	mem(h, -1);//初始化

	bool flag = true;//记录信息是否有矛盾
	while (n--)
	{
		cin.ignore(1024, '\n');//清空缓冲区
		sca("%s", str);

		if (!flag)continue;//如果有矛盾之后的建图都不需要了

		int x = str[0], y = str[2], z = str[3];

		if (!mp[x])mp[x] = ++k, ss[k] = x;//离散化,记录
		if (!mp[y])mp[y] = ++k, ss[k] = y;
		if (!mp[z])mp[z] = ++k, ss[k] = z;

		add(mp[y], mp[x]);//传入离散后的数字建边
		if (flag && dfs(mp[x], mp[y]))flag = false;
		//若无矛盾,查询能否反向走到,能就有自环,矛盾

		add(mp[x], mp[z]);
		if (flag && dfs(mp[z], mp[x]))flag = false;
	}

	if (flag)
		top_sort();
	else pri("No Answer");

	return 0;
}

看能力更新(

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值