算法入门练习2023/3/4

文章介绍了几个编程题目,包括安卓图案解锁的合法性判断,通过数组模拟思想解决;小红爱数数问题,利用map记录信息计算总数;奶牛发型问题,利用单调栈求解发型总和;以及一个模拟n皇后问题的简化版,检查皇后是否可以安全放置。
摘要由CSDN通过智能技术生成

1.安卓图案解锁

题目描述
栗主席(lizi)是某xxxx大学的一个不得了的程序猿,然而没想到吧,他竟然有女盆友,我们假设为QAQ!!!
那天,QAQ问栗子:你的小米5s的图像解锁密码到底是多少?
栗子:嘛?我仔细想想...
QAQ:你仿佛在逗我...
...
栗子:我的图像解锁用过好多次密码,后来都是用指纹解锁,所以忘记密码辣。但是我记得可能是那几个密码
QAQ:那你务必告诉我...
栗子: ...

然后,栗子就写下了一堆可能的密码,安卓图案解锁中,数字对应的位置已经标出。
但是栗子当然不想把真正的密码告诉QAQ,所以给QAQ的一系列的密码中,甚至有一些密码,是不符合安卓图案解锁的规则的。
QAQ也知道栗子肯定不老实,给了很多错的密码,甚至不符合规则的密码,所以想请你来找出,哪些密码是不符合规则的。


安卓图案解锁的密码有这样的一些特点:
1.每个数字最多只会被使用一次。
2.如果想直接连接两个数字,但是线段中会经过另一个数字,当且仅有那个数字已经在之前就被使用过了,才会合法。(比如你想从1直接连接到9,那么要么是1->3->9,要么是3在之前已经被使用过了,然后才能直接从1->9)
输入描述:
多组输入
每组输入占一行,包含一串数字(1~9),长度不超过30
输出描述:
输出这个安卓图案解锁是否合法,如果合法输出"YES",反之输出"NO" (请参照样例输出,不要输出引号)

正确代码:

#include<iostream>
#include<string.h>
using namespace std;
int main() {
   int a[9], b[9] = {1,2,1,3,0,3,1,2,1};
   string c;
   while (cin >> c) {
       int pt = 1;
       memset(a,0,sizeof(a));
       for (int i = 0; i < c.size()-1; i++) {
           int a1 = c[i] - '0';
           int a2 = c[i + 1] - '0';
           if (a1==a2){
               pt = 0;
               break;
           }
           if (a[a1 - 1]==1 || a[a2 - 1]==1) {
               pt = 0;
               break;
           }
           a[a1 - 1] = 1;
           if(b[a1 - 1]==b[a2 - 1] && !a[(a1+a2)/2-1]) {
               pt = 0;
               break;
           }
       }
       if (pt)
           cout << "YES" << endl;
       else cout << "NO" << endl;
   }
}

总结:这道题体现的是数组模拟的思想,选对方法很关键。

把特殊情况分开来,1.一个数字连续连接,

                                2.连了早就连过的数字(判断未连过,则刷新数组)

                                3.跳过中间数字连两端

2.小红爱数数

正确代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int g[2*100000+5];
map<ll,int>m1;
map<ll,int>m2;
int main()
{
    int n;ll sum=0;
    cin>>n;
    for(int i=1;i<=n;i++){
        int a;
        cin>>a;
        if(m1.find(a)!=m1.end())
        g[i]=g[m1.find(a)->second]+m2.find(a)->second*(i-m1.find(a)->second)+1;
        if(m1.find(a)==m1.end()){
            m1.insert(make_pair(a,i));
        }
        else {
            m1.find(a)->second=i;
        }
        if(m2.find(a)==m2.end()){
            m2.insert(make_pair(a,1));
        }
        else{
            m2.find(a)->second++;
        }
    }
    for(int i=1;i<=n;i++){
        sum+=g[i];
    }
    cout<<sum<<endl;
}

总结:该题要找到没两个相同字符间的联系,从而计算总数:

使用 map  m1记录上一个数字出现的位置(离散化后使用数组也可以),m2记录相同的数字有多少个, gi表示以 i为右端点时,所有满足条件的数对的距离总和是多少。转移:gi=g(k)+size*(i-k)+1, k为上一个相同数字。

3.奶牛发型(单调栈)

题目描述

一群身高不等的奶牛排成一排,向右看,每个奶牛只能看到身高小于自己的奶牛发型,问这些奶牛能够看到的发型总和是多少

自己写的代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int  main(){
    stack<ll>s;
    ll n,sum=0;
    cin>>n;
    for(int i=0;i<n;i++){
        ll c;
        cin>>c;
        while(!s.empty()&&s.top()<=c){
            s.pop();
        }
        sum+=s.size();
        s.push(c);
    }
    cout<<sum<<endl;
}

总结:首先弹出元素是因为它右边相邻牛比它高(看不到它的头发并且挡住了该牛的视线),“挡住”这个关键字很重要,这就是说该牛已经看不到后面的其他牛的头发啦,而思路一是按顺序比较身高,统计每头牛前面能看到它头发的牛数,既然该牛望不到后面,那么把它出栈对整个结果没有影响。

4.n皇后问题(假,模拟题)

皇后是国际象棋中实力最强的一种棋子,它可以攻击与它同行、同列、甚至斜向的其它棋子。

Komorebi最近刚刚了解到了n皇后问题,这个问题是这样的:
给你一个n×n的棋盘,有多少种放置方案可以让它们不能互相攻击。
然而这个问题对Komorebi还是太难了,也并不实用,现在他有一个n×n的空棋盘,并进行T次操作,他每次会试图在棋盘上的某一个格子放置一个皇后,如果这次放置不会使得有皇后互相攻击,那他就会将皇后放在这个位置,不然他就会放弃这次操作。

但因为棋盘太大了,每次他无法判断会不会使得有皇后互相攻击,你可以帮帮他吗?

自己写的代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    long n,t;
    cin>>n>>t;
    bool arr[n+1][n+1];
    for(long i=0;i<n+1;i++)
        for(int j=0;j<n+1;j++)
            arr[i][j]=false;
    for(long i=0;i<t;i++)
    {
        long x,y;
        cin>>x>>y;
        if(arr[x][y]==true)
        {
            cout<<"No"<<endl;
            continue;
        }
        else
        {
            cout<<"Yes"<<endl;
            for(long k=1;k<=n;k++)
            {
                arr[k][y]=true;//同一竖
                arr[x][k]=true;//同一行
            }
            long t1=x,t2=y;
            while(t1>=1&&t2>=1&&t1<=n&&t2<=n)
            {
                arr[t1++][t2--]=true;
            }
            t1=x,t2=y;
            while(t1>=1&&t2>=1&&t1<=n&&t2<=n)
            {
                arr[t1--][t2--]=true;
            }
            t1=x,t2=y;
            while(t1>=1&&t2>=1&&t1<=n&&t2<=n)
            {
                arr[t1--][t2++]=true;
            }
            t1=x,t2=y;
            while(t1>=1&&t2>=1&&t1<=n&&t2<=n)
            {
                arr[t1++][t2++]=true;
            }
        }
    }
}

正确代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
const int N=3*1e6+5;
int a[N],b[N],c[N],d[N];
int main()
{
    int n,T;
   scanf("%d%d",&n,&T);
    memset(a,false,sizeof(a));
    memset(b,false,sizeof(b));
    memset(c,false,sizeof(c));
    memset(d,false,sizeof(d));
    for(int i=0;i<T;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(a[x]==true || b[y]==true || c[x+y]==true || d[x-y]==true)
        {
            printf("No\n");
            continue;
        }
        else
        {
            printf("Yes\n");
            a[x]=true;
            b[y]=true;
            c[x+y]=true;
            d[x-y]=true;
        }
    }
}

总结:这道题套着N皇后的壳子,实际上只是一道模拟题。

在我自己写的代码中,桶排序完全没有必要,浪费时间也浪费空间,还容易造成内存冲突。

只需要标记行列,两斜(找跟坐标的关系),就行啦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值