CSP简单题题解 202206-2
本次题解为 2022-06-12 第26次CCF计算机软件能力认证
题目链接🔗 202206-2 重复局面 ,
题解在网络上发布:方便大家获取代码,自己上手删改试试看 ---- 建议题解Acwing(鉴别成本低,平均质量高)
有的第2题看上去比较吓唬人,但依据直观的思路拿到70分难度不大,剩余的30分通过仔细分析哪里带来了“复杂度”,观察数据本身的特征;可以产生一些类似于“剪枝”的巧思,或者从另一个思考角度来解决问题。
这个题没有用到任何的算法,就是细致的分析,仅此而已,时间复杂度没有降低。
70分 - 暴力解
- 仔细读题,题干中带来了重要信息(虽然有时候背景很长):
- 藏宝图B左下角位置一定是一棵树,即 A [ x ] [ y ] = B [ 0 ] 0 ] = 1 A[x][y] = B[0]0] =1 A[x][y]=B[0]0]=1 ----- 带来了迭代匹配的初始点
- 需要注意,最先输入的是(索引概念上的,与实际存储相反的) B [ S ] [ 0 ] ⋅ ⋅ ⋅ B [ S ] [ S ] B[S][0]···B[S][S] B[S][0]⋅⋅⋅B[S][S] 一行, B [ 0 ] [ 0 ] ⋅ ⋅ ⋅ B [ 0 ] [ S ] B[0][0]···B[0][S] B[0][0]⋅⋅⋅B[0][S] 一行最后输入
- 由于绿化图尺寸过大,输入数据中仅包含 n n n 棵树的坐标而非完整的地图 ----- 提示你时间复杂度来自于哪里
- 看上去是 O ( S × S × n ) O(S\times S\times n) O(S×S×n),实际上在 O ( L × L ) O(L\times L) O(L×L)
#include<iostream>
using namespace std;
struct Node{
int x;
int y;
};
int main()
{
int n, L, S;
cin>>n>>L>>S;
int count_B = 0;
int sum = 0;
Node *A = NULL;
A = new Node[n];
int *full_A = NULL;
full_A = new int[(L+1)*(L+1)];
int *full_B = NULL;
full_B = new int[(S+1)*(S+1)];
for(int i = 0; i<L+1; i++){
for(int j = 0; j<L+1; j++){
full_A[i*(L+1) + j] = 0; //时间复杂度的来源
}
}
int x,y;
for(int i = 0; i<n; i++){
cin>>x>>y;
A[i].x = x;
A[i].y = y;
full_A[x*(L+1) + y] = 1;
}
for(int i = 0; i<S+1; i++){
for(int j = 0; j<S+1; j++){
cin>>full_B[(S-i)*(S+1) + j]; //注意输入的顺序,行上的索引需要S-i
if(full_B[i*(S+1) + j]==1)count_B++;
}
}
int flag = 0;
for(int i = 0; i<n; i++){//以A中某棵树作为 B起始点-左下角
int base_Ax = A[i].x;
int base_Ay = A[i].y;
if(base_Ax > L-S || base_Ay > L-S)continue; //(B的左下角,0<=x,y<=L-S
//把B放到A中去匹配
int flag = 0;
for(int x = 0; x<S+1 && flag == 0; x++){
for(int y = 0; y<S+1 && flag == 0; y++){
if(full_B[x * (S+1) + y] == full_A[(base_Ax + x)*(L+1) + (base_Ay + y)]);
else flag = 1;
}
}
if(flag == 0)sum++;
}
cout<<sum<<endl;
return 0;
}
100分 解法1 微小改动
事实证明 O ( S × S × n ) O(S\times S\times n) O(S×S×n) 也是可以过的!构建A的方法有一定差异,带来了
#include<bits/stdc++.h>
using namespace std;
int n,l,s;
int x[1010],y[1010];
int graph[60][60];
int xx[2550],yy[2550];
int cnt;
int ans;
int main()
{
cin>>n>>l>>s;
for(int i = 0;i < n;i++)
cin>>x[i]>>y[i];
for(int i = s;i >= 0;i--) //逆(行)序的 输入形式
for(int j = 0;j <= s;j++)
cin>>graph[i][j];
for(int i = 0;i < n;i++) // 还是取了A的一个点 作为遍历的初始点
{
if(x[i] + s > l || y[i] + s > l)
continue;
int temp[60][60];
memset(temp,0,sizeof(temp));
for(int j = 0;j < n;j++){
//保证在B的范围大小内;构建了一个 大小为(S+1)*(S+1)的part_A
if(x[j] >= x[i] && x[j] <= x[i] + s && y[j] >= y[i] && y[j] <= y[i] + s)
temp[x[j] - x[i]][y[j] - y[i]] = 1;
}
int flag = 1;
for(int j = 0;j <= s;j++)
for(int k = 0;k <= s;k++)
if(temp[j][k] != graph[j][k]){
flag = 0;
break;
}
ans += flag;
}
cout<<ans;
return 0;
}
100分 解法2 — 观察数据
- 仔细观察数据上的说明 — 题干中也有提示
- 关于A的遍历((L+1) ×(L+1)为边界)带来了最大的复杂度 - 50分,观察了题干可以规避
- 其次是关于B的遍历((L+1) ×(L+1)为边界)- 70分 ;但其中的n是稀疏的
- 通过以A中存在的某棵🌲作为起始点;(作为B的左下角)到B中去遍历,看这棵🌲在不在B中
- 先判断①在不在B的区域中,在区域中②再看B中有没有这棵🌲
- ①判断 结果A不在范围内的被去除,② A有但B没有的被去除 - 不匹配 ③最后判断:B有A没有,通过计数判断
- 时间复杂度 1000 × 1000 < 1 亿 1000 × 1000 < 1亿 1000×1000<1亿
#include<iostream>
using namespace std;
struct Node{
int x;
int y; };
int main()
{
int n, L, S;
cin>>n>>L>>S;
int count_B = 0;
int sum = 0;
Node *A = NULL;
A = new Node[n];
int *full_B = NULL;
full_B = new int[(S+1)*(S+1)];
int x,y;
for(int i = 0; i<n; i++){
cin>>x>>y;
A[i].x = x;
A[i].y = y;
}
for(int i = 0; i<S+1; i++){
for(int j = 0; j<S+1; j++){
cin>>full_B[(S-i)*(S+1) + j]; //注意输入的顺序
if(full_B[(S-i)*(S+1) + j]==1)count_B++;
}
}
for(int i = 0; i<n; i++){ //以A中某棵树作为 B起始点-左下角
int base_Ax = A[i].x;
int base_Ay = A[i].y;
if(base_Ax > L-S || base_Ay > L-S)continue; //(保证B的左下角,0<=x,y<=L-S)
int count = 0;
//遍历A中的树,看是不是在B中;
for(int k = 0; k<n; k++){
//A中的棵树 在B的范围中
if(A[k].x >= base_Ax && A[k].x <= base_Ax+S && A[k].y >= base_Ay && A[k].y <= base_Ay+S){
//A中的这棵树在B上,增值;
if(full_B[(A[k].x - base_Ax)*(S+1) + (A[k].y - base_Ay)] == 1)count++;
//A中有,B没有,统计变得无效了
else count = -30000;
}
}
//会不会出现B中有1,但A中没有这棵树 - 不会,否则count_B > count
if(count == count_B)sum++;
}
cout<<sum<<endl;
return 0;
}