首页 > 其他分享 >P6491 [COCI2010-2011#6] ABECEDA

P6491 [COCI2010-2011#6] ABECEDA

时间:2023-11-21 18:12:10浏览次数:26  
标签:26 int len ++ ABECEDA P6491 now 2011 size

前言

思维难度:绿。

代码难度:绿/蓝。

综合:绿/蓝。

带来两种做法。主要是预处理的部分不同,所以就来水一篇。

传送门

前置芝士

分析

我们很容易想到通过输入去确定大概的大小。具体地,对于两字符串,若前 $i - 1$ 位相同,那么我们要么通过第 $i$ 位确定大小,要么第 $i$ 位相同去比较后面。但我们有多个字符串,容易想到,用 vector 存前 $i - 1$ 位相同的 $s[j][i]$。然后根据先后顺序去大致确定粗略的大小。

	for (int i = 0; i < len; i++) {
		string now = "";
		if (v.size()) v.clear();
		for (int j = 1; j <= n; j++) {
			if (s[j].size() < i + 1) continue;//若这一个字符串的大小小于当前枚举的位数,则跳过。
			v2[s[j][i] - 'a'] = 1;//记录在输入中出现过的字母。
			string sub = "";
			for (int k = 0; k < i; k++) sub = sub + s[j][k];
			if (sub != now) {//若前 i - 1 位不同,就进行处理。
				now = sub;
				for (int k = 0; k < (int)v.size() - 1; k++) //注意,一定是 (int)v.size() - 1,而不是 v.size() - 1,具体为什么可自行探究。
					if (v[k] != v[k + 1]) b[v[k] - 'a'][v[k + 1] - 'a'] = 1;//确定大致的大小关系。当然,也有其他写法
				v.clear();
			}
			v.push_back(s[j][i]);//存下当前位,与后面的字母比较
		}
		for (int k = 0; k < (int)v.size() - 1; k++) //最后可能有余下的,也要处理。
			if (v[k] != v[k + 1]) b[v[k] - 'a'][v[k + 1] - 'a'] = 1;
	}

然后若合法,我们这样可以将大连小连成一个有向无环图,如样例一。

由于学校机房太拉,做不了图,周末上图

若不合法,就如样例二,会有个环。

由于学校机房太拉,做不了图,周末上图

那么,我们就有两种方法去做。

利用 DFS

很容易,若图跑着跑着又回到了起点,那么一定又环,当然,我们不走重复的路,而且我们不用一次走完。然后,我们在走的时候,又可以将途经的点都给做标记,说明这个点小于起点,最终跑到出度为 $0$ 的点。

void dfs(int now, int x) {
	v1[x] = 1;//标记点到过了。
	if (x != now) b[now][x] = 1;
	for (int i = 0; i < 26; i++) {
		if (b[i][now]) {
			if (i == x) {//到了起点,说明有环。
				cout << '!' << '\n';
				exit(0);
			}
			if (!v1[i]) dfs(i, x);
		}
	}
}

然后我们去找每一个字母字典序大于多少个字母,然后记录下来,最后将记录下来的数组排序。若一个字母字典序大于多少个字母的个数已经有过了,那么就一定不能确定。代码如下。

	for (int i = 0; i < 26; i++) {
		int res = 0;
		memset(v1, 0, sizeof v1);
		dfs(i, i);
		for (int j = 0; j < 26; j++) 
			if (b[j][i]) res++;
		if (v2[i]) a[i].x = ++res;
		a[i].y = char(i + 'a');
		if (v3[res] && res != 0) {
			cout << '?' << '\n';
			return 0;
		}
		v3[res] = 1;
	}
	sort(a, a + 26, cmp);
	for (int i = 0; i < 26; i++) if (a[i].x != 0) cout << a[i].y;

利用拓扑排序

思想很简单。通过他去判环。若队列任意一刻,点的个数大于了 $1$,就有可能答案不确定,若有环,则一定有点的入度没减为零。

大佬讲的挺多,可以看一下他们的。我主要讲法一。

代码

法一

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

const int N = 110;
int n, len, d[N];
string s[N];
vector<char> v;
bool b[30][30], v1[N], v2[N], v3[N];
struct node {
	int x;
	char y;
} a[N];
bool cmp(node x, node y) {return x.x < y.x; }
void dfs(int now, int x) {
	v1[x] = 1;
	if (x != now) b[now][x] = 1;
	for (int i = 0; i < 26; i++) {
		if (b[i][now]) {
			if (i == x) {
				cout << '!' << '\n';
				exit(0);
			}
			if (!v1[i]) dfs(i, x);
		}
	}
}

int main() {
    // ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> s[i];
	len = 10;
	for (int i = 0; i < len; i++) {
		string now = "";
		if (v.size()) v.clear();
		for (int j = 1; j <= n; j++) {
			if (s[j].size() < i + 1) continue;//若这一个字符串的大小小于当前枚举的位数,则跳过。
			v2[s[j][i] - 'a'] = 1;//记录在输入中出现过的字母。
			string sub = "";
			for (int k = 0; k < i; k++) sub = sub + s[j][k];
			if (sub != now) {//若前 i - 1 位不同,就进行处理。
				now = sub;
				for (int k = 0; k < (int)v.size() - 1; k++) //注意,一定是 (int)v.size() - 1,而不是 v.size() - 1,具体为什么可自行探究。
					if (v[k] != v[k + 1]) b[v[k] - 'a'][v[k + 1] - 'a'] = 1;//确定大致的大小关系。当然,也有其他写法
				v.clear();
			}
			v.push_back(s[j][i]);//存下当前位,与后面的字母比较
		}
		for (int k = 0; k < (int)v.size() - 1; k++) //最后可能有余下的,也要处理。
			if (v[k] != v[k + 1]) b[v[k] - 'a'][v[k + 1] - 'a'] = 1;
	}
	for (int i = 0; i < 26; i++) {
		int res = 0;
		memset(v1, 0, sizeof v1);
		dfs(i, i);
		for (int j = 0; j < 26; j++) 
			if (b[j][i]) res++;
		if (v2[i]) a[i].x = ++res;
		a[i].y = char(i + 'a');
		if (v3[res] && res != 0) {
			cout << '?' << '\n';
			return 0;
		}
		v3[res] = 1;
	}
	sort(a, a + 26, cmp);
	for (int i = 0; i < 26; i++) if (a[i].x != 0) cout << a[i].y;
	return 0;
}

法二

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

const int N = 110;
int n, len, d[N];
string s[N];
vector<char> v;
vector<int> g[N], ans;

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> s[i];
	len = 10;
	for (int i = 0; i < len; i++) {
		string now = "";
		if (v.size()) v.clear();
		for (int j = 1; j <= n; j++) {
			if (s[j].size() < i + 1) continue;
			string sub = "";
			for (int k = 0; k < i; k++) sub = sub + s[j][k];
			if (sub != now) {
				now = sub;
				for (int k = 0; k < (int)v.size() - 1; k++) 
					if (v[k] != v[k + 1]) g[(int)v[k] - 'a'].push_back((int)v[k + 1] - (int)'a'), d[v[k + 1] - 'a']++;
                    //这代码换种写法
				v.clear();
			}
			v.push_back(s[j][i]);
		}
		for (int k = 0; k < (int)v.size() - 1; k++) 
			if (v[k] != v[k + 1]) g[v[k] - 'a'].push_back(v[k + 1] - 'a'), d[v[k + 1] - 'a']++;
	}
	queue<int> q;//拓扑
	int f = 0;
	for (int i = 0; i < 26; i++) {
		if (!d[i] && g[i].size()) q.push(i);
	}
	while (q.size()) {
		if (q.size() > 1) f = 1;
		int now = q.front();
		q.pop();
		ans.push_back(now);
		for (int to : g[now]) {
			d[to]--;
			if (!d[to]) q.push(to);
		}
	}
//	cout << d[9];
	for (int i = 0; i < 26; i++) if (d[i]) f = 2;
//	cout << f;
	if (!f) for (int i : ans) cout << char(i + 'a');
	if (f == 2) cout << '!';
	if (f == 1) cout << '?';
	return 0;
}

后记

法一自己想的,法二看了题解。

另外,我想过并查集,但不会,希望有大佬打脸。

标签:26,int,len,++,ABECEDA,P6491,now,2011,size
From: https://www.cnblogs.com/luckycloud/p/17847243.html

相关文章

  • P5482 [JLOI2011] 不等式组
    P5482[JLOI2011]不等式组这道题比板子还是难不少,因为有大量的分类讨论。看到题就可以考虑平衡树了。\(ax+b>c\iffax>c-b\),根据不等式乘除法的变号规则分类。\(a>0\),不等号方向不变,\(x>\dfrac{c-b}{a}\)。\(a<0\),不等号方向改变,\(x<\dfrac{c-b}{a}\)。\(a=0\),\(0>c-b\iff......
  • 【洛谷 P1307】[NOIP2011 普及组] 数字反转 题解(字符串)
    [NOIP2011普及组]数字反转题目描述给定一个整数,请将该数各个位上数字反转得到一个新数。新数也应满足整数的常见形式,即除非给定的原数为零,否则反转后得到的新数的最高位数字不应为零(参见样例2)。输入格式一个整数。输出格式一个整数,表示反转后的新数。样例#1样例输入#1123样......
  • P3513 [POI2011] KON-Conspiracy
    题目描述:Byteotia的领土被占领了,国王Byteasar正在打算组织秘密抵抗运动。国王需要选一些人来进行这场运动,而这些人被分为两部分:一部分成为同谋者活动在被占领区域,另一部分是后勤组织在未被占领的领土上运转。但是这里出现了一个问题:后勤组织里的任意两人都必须是熟人,以促进合作......
  • P2486 [SDOI2011] 染色
    题目描述给定一棵\(n\)个节点的无根树,共有\(m\)个操作,操作分为两种:将节点\(a\)到节点\(b\)的路径上的所有点(包括\(a\)和\(b\))都染成颜色\(c\)。询问节点\(a\)到节点\(b\)的路径上的颜色段数量。颜色段的定义是极长的连续相同颜色被认为是一段。例如112221......
  • [POI2011] SMI-Garbage 题解
    题目链接显然,对于初始颜色与目标颜色不同的边,我们需要走过奇数次;对于初始颜色与目标颜色相同的边,我们需要走过偶数次。对于只有偶数边的情况,这种情况下不走就行;对于只有奇数边;可以理解为每条边只能经过一次,就是欧拉路径问题,并且考虑这题的特殊性质,如果一个图是由若干个简单环构......
  • yum源修改基于CentOS Linux release 8.3.2011
    查看系统版本:(8的镜像源都可以用不用分小版本)cat/etc/redhat-release修改centos文件内容sed-i's/mirrorlist/#mirrorlist/g'/etc/yum.repos.d/CentOS-*sed-i's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g'/etc/yum.repos.d/CentOS......
  • 2011年春季-C语言课程设计-指导书
    C语言课程设计指导书注:请各班学习委员按学号顺序对本班同学进行分组(不允许同学自行组合),把后面所列的题目分割开交给各组保留,并组织同学按时上机。1.总体要求1)       按照名单上的顺序分配PC,按照学号的顺序每3人一组(如果剩余2人,则选择任务11;如果剩余1人,则分散到前面的组中......
  • 2011年春季-C语言课程设计-报告格式
    以下内容根据教务处最新要求制定,请严格遵守。附件:课程设计报告的内容及其文本格式1、课程设计报告要求用16k纸排版,单面打印,并装订成册,内容包括:除封面外,其他每页的页脚需要有页码。①封面(包括题目、院系、专业班级、学生学号、学生姓名、指导教师姓名、职称、起止时间等)②设计任务及......
  • 洛谷P3522/POI2011 TEM-Temperature
    涉及知识点:单调队列、贪心、递推前言最近找了点单调队列的题练练手,就遇到这道题,本题对于单调队列弹队尾的时候有别于普通单调队列,一些题解并没有写的很清楚,因此写下这篇题解。题面Link你有一个长度为\(n\)的序列\(A\),告诉你序列中每一个数\(A_i\)的取值范围\(down_i\)......
  • P3217 [HNOI2011] 数矩形
    P3217[HNOI2011]数矩形题解前言提交记录本题其实并不是非常难想,那么为什么本蒟蒻还交了那么多发呢?cal函数求平方的时候传值未开longlong,我谔谔。正文题面省流:给定$n$个点求最大举行的面积,矩形的边可以不与坐标系垂直。如果每次枚举矩形的四个点的话,$O\left(n^4\rig......