CSP简单题题解 202206-2 for CUP 20级CSP冲刺班

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值