算法每日一题(Overrandomized)

题目来源:2020 Code Jam Round 1C Overrandomized.
日期:[2020年5月2日]
个人题集编号:[A4-1]
知识点:[思维、集合]
原题面:提交+官方解析
题意简述
某台服务器可以执行对数字 0~9 的哈希转换,具体操作就是它有一个由不同大写字母构成的长度为 10 的字符串 D,D[ k ] 就代表数字 k 可以用大写字母 D[k] 代替。
现在我们尝试通过 1e4 次询问,来确定字符串 D。
每一次询问具体步骤如下:

  • 输入一个整数 M (M 是小于 U 位数,其实几位数不重要)给服务器;
  • 服务器会随机选一个整数 N ,1 <= N <= M,我们不知道 N 具体值;
  • 服务器返回一个字符串 R ,代表 N 每个位置上一一替换后的字符串。(N无前导零)

现在我们知道所有的 M 和所有的 R,目标是确定长度为 10 的字符串 D。
其中 2<=U<=16 。

解题思路
这题的题意可能很绕,而且数据上面没说全,本题共 3 个点,其中第一二个点如上所述,但是第三个点我们是不知道每次询问的 M 的,也就是说只知道服务器的回答 R,而 M 虽然存在却不告诉我们。
这样一来第一二个点和第三个点就完全是两种解题思路,我只做出来前两问,官网有完整解法。

对于前两问,我的思路是如果我能确定 1,我就可以通过排除 1 的字母,来确定 2(因为D 中所有字母都是不同的)的字母;于是我可以通过将 1 所有可能匹配的字母放在一个集合内(只有一个,因为 1<=N<=1时只有一种取法),2 所有可能匹配的字母放在第二个集合内(最多有两个字母,因为 N 只能取 1 或 2),3~9 依次类推。
单个元素的情况很好考虑,直接压入集合即可,下面考虑不是单个元素的情况。假设 ‘37 HR’ 那么 H 一定是属于 3 的集合内的,因为十位只能取1、2、3,而个位却可能取0~9,所以 R 的作用就小很多,但是却可以压入 0 对应的集合(因为没有前导零,我们只能通过非首位来确定 0 的集合)。而如果 ‘35 K’这时 K 就可能对应 0~9 的任意一个数了,我们忽略。

通过上述方法就可以确定 0~9 的所有可能的取值集合,我们的确定顺序是 1 至 9 再到 0。当然你可能会有疑问“如果所给数据不能通过首位确定 1~9 咋办,那岂不是要通过非首位确定?而我们又舍弃了非首位…。”我刚开始也有这种担忧,但是题意既然说必定有解,如果通过非首位确定集合,这个时候可能就有多解了,可以举个例子证明一下。

第三点粗略的看了一眼,好像牵扯到概率和某些数学定理,时间有限,就到此为止。

代码示例
只能过前两个点!!!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e4;
set<char> s[11],s2[11];
bool vis[150];
void work(char x[],char op[]){
	int n = strlen(x), m = strlen(op);
	if(n != m) return;
	s[x[0]-'0'].insert(op[0]);
	for(int i = 1;i < n;i++) s2[x[i]-'0'].insert(op[i]);
	//printf("%d %c\n",x[0]-'0',op[0]);
	
}
void solve(){
	for(int i = 0;i < 10;i++) s[i].clear(),s2[i].clear();
	char x[30], op[20], ans[30];
	int u; memset(vis,0,sizeof vis);
	scanf("%d",&u);
	for(int i = 1;i <= N;i++){
		scanf("%s%s",x,op);
		work(x,op);
	}
	for(int i = 0;i < 10;i++)
		for(set<char>::iterator it = s[i].begin();it != s[i].end();it++){
			if(!vis[*it]) ans[i] = *it, vis[*it] = true;
		}
	for(set<char>::iterator it = s2[0].begin();it != s2[0].end();it++)
		if(!vis[*it]) ans[0] = *it, vis[*it] = true;
	for(int i = 0;i <= 9;i++) putchar(ans[i]);
	puts("");
}
int main(){
	
	int t; scanf("%d",&t);
	for(int c = 1;c <= t;c++){
		printf("Case #%d: ",c);
		solve();
	}
	return 0;
}
已标记关键词 清除标记
【为什么还需要学习C++?】 你是否接触很多语言,但从来没有了解过编程语言的本质? 你是否想成为一名资深开发人员,想开发别人做不了的高性能程序? 你是否经常想要窥探大型企业级开发工程的思路,但苦于没有基础只能望洋兴叹?   那么C++就是你个人能力提升,职业之路进阶的不二之选。 【课程特色】 1.课程共19大章节,239课时内容,涵盖数据结构、函数、类、指针、标准库全部知识体系。 2.带你从知识与思想的层面从0构建C++知识框架,分析大型项目实践思路,为你打下坚实的基础。 3.李宁老师结合4大国外顶级C++著作的精华为大家推出的《征服C++11》课程。 【学完后我将达到什么水平?】 1.对C++的各个知识能够熟练配置、开发、部署; 2.吊打一切关于C++的笔试面试; 3.面向物联网的“嵌入式”和面向大型化的“分布式”开发,掌握职业钥匙,把握行业先机。 【面向人群】 1.希望一站式快速入门的C++初学者; 2.希望快速学习 C++、掌握编程要义、修炼内功的开发者; 3.有志于挑战更高级的开发项目,成为资深开发的工程师。 【课程设计】 本课程包含3大模块 基础篇 本篇主要讲解c++的基础概念,包含数据类型、运算符等基本语法,数组、指针、字符串等基本词法,循环、函数、类等基本句法等。 进阶篇 本篇主要讲解编程中常用的一些技能,包含类的高级技术、类的继承、编译链接和命名空间等。 提升篇: 本篇可以帮助学员更加高效的进行c++开发,其中包含类型转换、文件操作、异常处理、代码重用等内容。
©️2020 CSDN 皮肤主题: 程序猿惹谁了 设计师:白松林 返回首页