一.数位和
题目描述
本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。
数学家高斯很小的时候就天分过人。一次老师指定的算数题目是:
1+2+...+100。
高斯立即做出答案:5050!
这次你的任务是类似的。但并非是把一个个的数字加起来,而是对该数字的每一个数位作累加。
这样从 1 加到 100 的“和”是:901;
从 10 加到 15 是:21,也就是:1+0+1+1+1+2+1+3+1+4+1+5,这个口算都可以出结果的。
按这样的“加法”,从 1 加到 1000 是多少呢?
运行限制
- 最大运行时间:1s
- 最大运行内存: 128M
思路:枚举+数位计算,水题。
#include <iostream>
using namespace std;
int main()
{
int sum = 0, temp, i;
for(i = 1; i <= 1000; ++ i)
{
temp = i;
while(temp)
{
sum += temp % 10;
temp /= 10;
}
}
cout << sum;
return 0;
}
二.数字划分
题目描述
本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。
W星球的长老交给小明一个任务: 1,2,3⋯16 这 16 个数字分为两组。
要求:这两组数字的和相同,并且,两组数字的平方和也相同,并且,两组数字的立方和也相同。
请你利用计算机的强大搜索能力解决这个问题。 并输出 1 所在的那个分组的所有数字。
这些数字要从小到大排列,两个数字间用一个空格分开。 即类似:1 4 5 8... 这样的答案。
运行限制
- 最大运行时间:1s
- 最大运行内存: 128M
思路:DFS+减枝。
#include <iostream>
#include <vector>
using namespace std;
bool DFS(int start, vector<int>& ans, int s, int ss, int sss, int& half_s, int& half_ss, int& half_sss)
{
if(s > half_s || ss > half_ss || sss > half_sss)
{
return false;
}
if(start == 17)
{
if(s == half_s && ss == half_ss && sss == half_sss)
{
for(int i = 0; i < ans.size(); ++ i)
{
cout << ans[i] << " ";
}
cout << endl;
return true;
}
return false;
}
for(int i = start; i <= 16; ++ i)
{
if(DFS(i + 1, ans, s, ss, sss, half_s, half_ss, half_sss))
{
return true;
}
ans.push_back(i);
if(DFS(i + 1, ans, s + i, ss + i * i, sss + i * i * i, half_s, half_ss, half_sss))
{
return true;
}
ans.pop_back();
}
return false;
}
int main()
{
int half_s = 0, half_ss = 0, half_sss = 0;
for(int i = 1; i <= 16; ++ i)
{
half_s += i;
half_ss += i * i;
half_sss += i * i * i;
}
half_s >>= 1;
half_ss >>= 1;
half_sss >>= 1;
vector<int> ans;
ans.push_back(1);
DFS(2, ans, 1, 1, 1, half_s, half_ss, half_sss);
return 0;
}
三.树形显示
题目描述
本题为代码补全填空题,请将题目中给出的源代码补全,并复制到右侧代码框中,选择对应的编译语言(C/Java)后进行提交。若题目中给出的源代码语言不唯一,则只需选择其一进行补全提交即可。复制后需将源代码中填空部分的下划线删掉,填上你的答案。提交后若未能通过,除考虑填空部分出错外,还需注意是否因在复制后有改动非填空部分产生错误。
对于分类结构可以用树形来形象地表示。比如:文件系统就是典型的例子。
树中的结节具有父子关系。我们在显示的时候,把子项向右缩进(用空格,不是tab),并添加必要的连接线,以使其层次关系更醒目。
下面的代码就是为了这个目的的,请仔细阅读源码,并填写划线部分缺少的代码。
源代码
Java
import java.util.*;
class MyTree
{
private Map<String, List<String>> map_ch = new HashMap<String, List<String>>();
private Map<String,String> map_pa = new HashMap<String,String>();
//增加父子级关系
public void add(String parent, String child)
{
map_pa.put(child, parent);
List<String> lst = map_ch.get(parent);
if(lst==null){
lst = new ArrayList<String>();
map_ch.put(parent, lst);
}
lst.add(child);
}
//拿到父亲节点
public String get_parent(String me){
return map_pa.get(me);
}
//拿到孩子节点
public List<String> get_child(String me){
return map_ch.get(me);
}
//生成空格
private String space(int n)
{
String s = "";
for(int i=0; i<n; i++) s += '.';
return s;
}
//判断x是否是所属子树的最后一个节点
private boolean last_child(String x){
String pa = map_pa.get(x);
if(pa==null) return true;
List<String> lst = map_ch.get(pa);
return lst.get(lst.size()-1).equals(x);
}
public void show(String x){
String s = "+--" + x;
String pa = x;
while(true){
pa = map_pa.get(pa);
if(pa==null) break;
s = ___________________________________;
}
System.out.println(s);
}
public void dfs(String x){
show(x);
List<String> lst = map_ch.get(x);
if(lst==null) return;
for(String it: lst){
dfs(it);
}
}
}
public class Main
{
public static void main(String[] args)
{
MyTree tree = new MyTree();
tree.add("root", "dog");
tree.add("root", "cat");
tree.add("root", "duck");
tree.add("dog", "AAdog");
tree.add("dog", "BBdog");
tree.add("dog", "CCdog");
tree.add("AAdog", "AAdog01");
tree.add("AAdog", "AAdog02");
tree.add("cat", "XXcat");
tree.add("cat", "YYcat");
tree.add("XXcat","XXcat-oo");
tree.add("XXcat","XXcat-qq");
tree.add("XXcat-qq", "XXcat-qq-hahah");
tree.add("duck", "TTduck");
tree.add("TTduck", "TTduck-001");
tree.add("TTduck", "TTduck-002");
tree.add("TTduck", "TTduck-003");
tree.add("YYcat","YYcat.hello");
tree.add("YYcat","YYcat.yes");
tree.add("YYcat","YYcat.me");
tree.dfs("root");
}
}
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
思路:
注意:本题样例和实际有冲突,space函数本来应是生成空格,但是代码中是用'.'表示空格
1.看命令行输出可以发现,本题中的add函数就是用来增加父子级关系的。
2.注释掉填空部分会发现,所有节点全部都是顶格输出,因此我们缺少对空格的输出。观察命令行输出可以发现,每四个空格表示一级关系。代码中的space函数就是用来生成n个空格的。
s = space(4) + s; // 填空
3.别问为啥,俺也没搞懂,这样例真的迷。。。答案如下:
import java.util.*;
class MyTree
{
// 父节点 -- 子节点列表
private Map<String, List<String>> map_ch = new HashMap<String, List<String>>();
// 子节点 -- 父节点
private Map<String,String> map_pa = new HashMap<String,String>();
// 增加 父 子 关系
public void add(String parent, String child)
{
map_pa.put(child, parent);
List<String> lst = map_ch.get(parent);
if(lst==null){
lst = new ArrayList<String>();
map_ch.put(parent, lst);
}
lst.add(child);
}
// 拿到父节点
public String get_parent(String me){
return map_pa.get(me);
}
// 拿到子节点
public List<String> get_child(String me){
return map_ch.get(me);
}
//生成......
private String space(int n)
{
String s = "";
for(int i=0; i<n; i++) s += '.';
return s;
}
//判断x是否是其父亲的最后一个节点
private boolean last_child(String x){
String pa = map_pa.get(x);
if(pa==null) return true;
List<String> lst = map_ch.get(pa);
return lst.get(lst.size()-1).equals(x);
}
//显示x
public void show(String x){
String s = "+--" + x;
String pa = x;
while(true){// 判断节点x的深度
pa = map_pa.get(pa);
if(pa==null) break;
s = (last_child(pa)? "." : "|") + space(4) + s; // 填空
}
System.out.println(s);
}
//遍历x
public void dfs(String x){
show(x);
List<String> lst = map_ch.get(x);
if(lst==null) return;
for(String it: lst){
dfs(it);
}
}
}
public class Main
{
public static void main(String[] args)
{
MyTree tree = new MyTree();
tree.add("root", "dog");
tree.add("root", "cat");
tree.add("root", "duck");
tree.add("dog", "AAdog");
tree.add("dog", "BBdog");
tree.add("dog", "CCdog");
tree.add("AAdog", "AAdog01");
tree.add("AAdog", "AAdog02");
tree.add("cat", "XXcat");
tree.add("cat", "YYcat");
tree.add("XXcat","XXcat-oo");
tree.add("XXcat","XXcat-qq");
tree.add("XXcat-qq", "XXcat-qq-hahah");
tree.add("duck", "TTduck");
tree.add("TTduck", "TTduck-001");
tree.add("TTduck", "TTduck-002");
tree.add("TTduck", "TTduck-003");
tree.add("YYcat","YYcat.hello");
tree.add("YYcat","YYcat.yes");
tree.add("YYcat","YYcat.me");
tree.dfs("root");
}
}
四.小数第n位
题目描述
我们知道,整数做除法时,有时得到有限小数,有时得到无限循环小数。
如果我们把有限小数的末尾加上无限多个 0,它们就有了统一的形式。
本题的任务是:在上面的约定下,求整数除法小数点后的第 n 位开始的 3 位数。
输入描述
输入一行三个整数:a b n,用空格分开。a 是被除数,b 是除数,n 是所求的小数后位置(0<a,b,n<10^9)
输出描述
输出一行 3 位数字,表示:a 除以 b,小数后第 n 位开始的 3 位数字。
输入输出样例
示例
输入
1 8 1
输出
125
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
思路:朴素的想法无非就是模拟竖式计算。
#include <iostream>
using namespace std;
int main()
{
int a, b, n;
cin >> a >> b >> n;
a %= b;
while(n >= 2)
{
-- n;
a *= 10;
a %= b;
}
for(int i = 1; i <= 3; ++ i)
{
a *= 10;
cout << a / b;
a %= b;
}
return 0;
}
本题的数据规模高达10^9,直接模拟很容易超时。回到模拟本身,我们之所以要模拟是因为本题数据的位数会很高,我们是没有办法直接存储下来并做运算的。对于循环小数,只要我们获得了循环节就可以很快解决,但这种方法并不能处理不循环小数。我们并不是只能1位1位手动计算,我们可以一次计算多位,再用1位进行微调。
例如计算1/7的小数点后第13位,我们可以先将被除数*10变为1000000000/7=1428571428(相当于把前十位小数移动到小数点前面),于是被除数变为1000000000%7=4,接下来就是再除3次了。
这样我们就可以将复杂度从O(10^9)降低为O(10^8)。
#include <iostream>
using namespace std;
int main()
{
long long a, b, n;
const long long mul = 1e10;
cin >> a >> b >> n;
a %= b;
while(n > 10)
{
n -= 10;
a *= mul;
a %= b;
}
while(n > 1)
{
n --;
a *= 10;
a %= b;
}
for(int i = 1; i <= 3; ++ i)
{
a *= 10;
cout << a / b;
a %= b;
}
return 0;
}
五.分考场
题目描述
n 个人参加某项特殊考试。
为了公平,要求任何两个认识的人不能分在同一个考场。
求是少需要分几个考场才能满足条件。
输入描述
输入格式:
第一行,一个整数 n (1≤n≤100),表示参加考试的人数。
第二行,一个整数 m,表示接下来有 m 行数据。
以下 m 行每行的格式为:两个整数 a,b,用空格分开 (1≤a,b≤n )表示第 a 个人与第 b 个人认识。
输出描述
输出一行一个整数,表示最少分几个考场。
输入输出样例
示例
输入
5
8
1 2
1 3
1 4
2 3
2 4
2 5
3 4
4 5
输出
4
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
思路:
[误区]对认识关系建模,用关系边表示,于是可以用并查集对认识关系进行维护。最终利用并查集求出认识关系的最大集合,其大小(最多互相认识的人)就是答案。
这种错误在于,1-3认识,1-2认识不代表2-3认识,而如果用并查集则默认2-3认识,与题意不符。
思路:本题实际上是个染色问题,将数据建成图之后,要求相邻节点之间不能用重复的颜色标识,求最少的颜色数。
可以用 二分答案 + DFS染色判断。
邻接矩阵版:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 110;
int adjRect[N][N], n;
int Color[N];
bool checkColor(int p, int c)
{
for (int i = 1; i <= n; ++i)
{
if (adjRect[p][i] && c == Color[i])
{
return false;
}
}
return true;
}
bool DFS(int p, int color, int colorNum)
{
int i, c;
for (i = 1; i <= n; ++i)
{
if (p == i || adjRect[i][p] == 0 || Color[i])
{
continue;
}
for (c = 1; c <= colorNum; ++c)
{
if (c != color && checkColor(i, c))
{
Color[i] = c;
if (DFS(i, c, colorNum))
{
break;
}
Color[i] = 0;
}
if (c == colorNum)
{
return false;
}
}
}
return true;
}
bool check(int colorNum)
{
memset(Color, 0, sizeof(Color));
for (int i = 1; i <= n; ++i)
{
if (!Color[i])
{
Color[i] = 1;
if (!DFS(i, 1, colorNum))
{
return false;
}
}
}
return true;
}
int main()
{
int m, i, a, b, L, R, M, ans;
cin >> n >> m;
L = 1;
R = n;
for (i = 1; i <= m; ++i)
{
cin >> a >> b;
adjRect[a][b] = adjRect[b][a] = 1;
}
while (L <= R)
{
M = (L + R) >> 1;
if (check(M))
{
ans = M;
R = M - 1;
}
else
{
L = M + 1;
}
}
cout << ans;
return 0;
}
链式前向星版(邻接表):
#include <iostream>
#include <cstring>
using namespace std;
struct Edge{
int to, next;
};
Edge edges[10010];
int cnt, Head[110], Color[110];
void add(int from, int to)
{
edges[++ cnt].to = to;
edges[cnt].next = Head[from];
Head[from] = cnt;
}
bool checkColor(int p, int color)
{
int e, to;
for(e = Head[p]; e != 0; e = edges[e].next)
{
to = edges[e].to;
if(color == Color[to])
{
return false;
}
}
return true;
}
bool DFS(int p, int color, int colorNum)
{
int to, e, c;
for(e = Head[p]; e != 0; e = edges[e].next)
{
to = edges[e].to;
if(!Color[to]) // 该邻接点还未上色
{
for(c = 1; c <= colorNum; ++ c)
{
if(c != color && checkColor(to, c)) // 检查to上色的合法性
{
Color[to] = c;
if(DFS(to, c, colorNum))
{
break;
}
Color[to] = 0;
}
if(c == colorNum) // 找不到合法的上色方案
{
return false;
}
}
}
}
return true;
}
bool check(int colorNum, int n)
{
memset(Color, 0, sizeof(Color));
for(int i = 1; i <= n; ++ i)
{
if(!Color[i])
{
Color[i] = 1;
if(!DFS(i, 1, colorNum))
{
return false;
}
}
}
return true;
}
int main()
{
int n, m, i, a, b, L, R, M, ans;
cin >> n >> m;
for(i = 1; i <= m; ++ i)
{
cin >> a >> b;
add(a, b);
add(b, a);
}
L = 1, R = n;
while(L <= R)
{
M = (L + R) >> 1;
if(check(M, n))
{
ans = M;
R = M - 1;
}
else
{
L = M + 1;
}
}
cout << ans;
}
由于颜色会很多,用DFS染色法直接判断会很费时间,因此上述代码会超时。还有一种在染色过程中及时动态增加颜色种数的方案。
思路:我们对每一个学生枚举要放置到哪一个教室,DFS+减枝。
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 110;
// 邻接矩阵 教室与其安排的学生 教室里已有的学生人数
int acquaintances[N][N], ans = 0x3f3f3f3f, student[N][N], classCapability[N], n;
void DFS(int stu, int classNum)// 当前要安排的学生 当前的教室数量
{
if (classNum >= ans)//当前安排的教室数量大于等于已有的答案,不需要再考虑了 减枝
{
return;
}
if (stu == n + 1)// 学生安排完了
{
ans = min(ans, classNum);//更新教室使用的个数
return;
}
int classId, stuId;
for (classId = 1; classId <= classNum; ++classId)
{
for (stuId = classCapability[classId]; stuId >= 1; --stuId)
{
if (acquaintances[stu][student[classId][stuId]])//冲突,换一间教室
{
break;
}
if (stuId == 1)//和这个教室里的学生不冲突
{
++classCapability[classId];
student[classId][classCapability[classId]] = stu;
DFS(stu + 1, classNum);
--classCapability[classId];
}
}
}
//还可以为这个学生放置到另一间空教室
//贪心(把当前不冲突的放进集合)不一定能得到最优解,原因在于,可能存在很多j>i,j与i不冲突,但与k<i冲突,此时如果把i与k放在一个集合,那么j只能令开集合。但是如果把i单独成集合,接下来就可以往这个集合里放j。所以必须考虑i单独开一个集合的情况
++classCapability[classNum + 1];
student[classNum + 1][classCapability[classNum + 1]] = stu;
DFS(stu + 1, classNum + 1);
--classCapability[classNum + 1];
}
int main()
{
int m, a, b, i;
scanf("%d%d", &n, &m);
for (i = 1; i <= m; ++i)
{
scanf("%d%d", &a, &b);
acquaintances[a][b] = acquaintances[b][a] = 1;
}
DFS(1, 0);
printf("%d",ans);
return 0;
}
六.合根植物
题目描述
w 星球的一个种植园,被分成 m×n 个小格子(东西方向 m 行,南北方向 n 列)。每个格子里种了一株合根植物。
这种植物有个特点,它的根可能会沿着南北或东西方向伸展,从而与另一个格子的植物合成为一体。
如果我们告诉你哪些小格子间出现了连根现象,你能说出这个园中一共有多少株合根植物吗?
输入描述
第一行,两个整数 m,n,用空格分开,表示格子的行数、列数(1≤m,n≤1000)。
接下来一行,一个整数 k(0≤k≤10^5 ),表示下面还有 kk 行数据。
接下来 k 行,每行两个整数 a,b,表示编号为 a 的小格子和编号为 b 的小格子合根了。
格子的编号一行一行,从上到下,从左到右编号。
比如:5×4 的小格子,编号:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
17 18 19 20
输出描述
输出植物数量。
输入输出样例
示例
输入
5 4
16
2 3
1 5
5 9
4 8
7 8
9 10
10 11
11 12
10 14
12 16
14 18
17 18
15 19
19 20
9 13
13 17
输出
5
样例说明
其合根情况参考下图:
运行限制
- 最大运行时间:2s
- 最大运行内存: 256M
思路:并查集裸题。。。不过需要将二维坐标转成一维索引再套并查集模板。
#include <iostream>
#include <cstring>
using namespace std;
int father[1000010];
int Find(int x)
{
return father[x] != x ? father[x] = Find(father[x]) : x;
}
void Union(int A, int B, int& ans)
{
A = Find(A);
B = Find(B);
if(A != B)
{
father[A] = B;
-- ans;
}
}
int main()
{
int m, n, i, u, v, ans;
cin >> m >> n;
ans = n = n * m;
for(i = 1; i <= n; ++ i)
{
father[i] = i;
}
cin >> m;
for(i = 1; i <= m; ++ i)
{
cin >> u >> v;
Union(u, v, ans);
}
cout << ans;
return 0;
}