AcWing寒假每日一题2022-第1周

2058.笨拙的手指


一、问题描述

奶牛贝茜正在学习如何在不同进制之间转换数字。但是她总是犯错误,因为她无法轻易的用两个前蹄握住笔。每当贝茜将数字转换为一个新的进制并写下结果时,她总是将其中的某一位数字写错。

例如,如果她将数字 14转换为二进制数,那么正确的结果应为 1110,但她可能会写下 0110或 1111。贝茜不会额外添加或删除数字,但是可能会由于写错数字的原因,写下包含前导0的数字。

给定贝茜将数字 N转换为二进制数字以及三进制数字的结果,请确定 N 的正确初始值(十进制表示)。

输入格式:
第一行包含 N的二进制表示,其中一位是错误的。
第二行包含 N的三进制表示,其中一位是错误的。

输出格式:
输出正确的 N的值。

数据范围:
0≤N≤109,且存在唯一解。

输入样例:
1010
212

输出样例:
14

样例解释:
14在二进制下的正确表示为 1110,在三进制下的正确表示为 112。


二、分析与代码

分析
先将二进制修改一位所有的十进制找出,再将三进制修改一位所有的十进制数找出,之后找到在两集合中相同的十进制数。

难点
输入时各个数字之间无空格隔开,如何将二/三进制数的各位分别保存。

方法
1.可以用一个整型读入,之后用除法与求余数将该十进制数各位分开再分别保存;
2.将二/三进制数的每位当作字符存储,再将每一位对应的整型数字分别存储
3.或者不将各位分别保存,而是将二/三进制数当字符串存储在string变量中,更改其中一位后利用秦九韶算法将其直接转换为十进制。

具体代码1(使用方法2)

#include <iostream>
#include <cstdio>
#include <vector>
#include <math.h>

using namespace std;

int reserve(int x)      //遍历0与1
{
    return (x+1)%2;
}

int reserve1(int x)     //遍历0、1、2
{
    return (x+1)%3;
}

int main()
{
    int i,j,k;
    
    vector <char> a;    //读入二进制数
    char x;
    scanf("%c",&x);
    while(x!='\n')
    {
        a.push_back(x);
        scanf("%c",&x);
    }
    vector <int> two;   //将二进制数按int形式存储
    for(i=0;i<a.size();i++)
		two.push_back((int)(a[a.size()-i-1]-48));
    
    vector <int> count1(a.size(),0);    //存储二进制对应所有可能的十进制值
    j=0;
    for(i=0;i<a.size();i++)
    {
        two[i]=reserve(two[i]);
        for(k=0;k<two.size();k++)
            count1[j]=count1[j]+two[k]*pow(2,k);
        j++;
        two[i]=reserve(two[i]);
    }
    
    char y;
    vector <char> b;
    scanf("%c",&y);
    while(y!='\n')
    {
        b.push_back(y);
        scanf("%c",&y);
    }
    vector <int> three;
    
    for(i=0;i<b.size();i++)
		three.push_back((int)(b[b.size()-i-1]-48));
	
    
    vector <int> count2(b.size()*2,0);
    j=0;

    for(i=0;i<b.size();i++)
    {
        three[i]=reserve1(three[i]);
        for(k=0;k<three.size();k++)
            count2[j]=count2[j]+three[k]*pow(3,k);
        j++;
        three[i]=reserve1(three[i]);
        for(k=0;k<three.size();k++)
            count2[j]=count2[j]+three[k]*pow(3,k);
        j++;
        three[i]=reserve1(three[i]);
    }
    
    for(i=0;i<count1.size();i++)        //查找二进制与三进制对应的相同十进制值
        for(j=0;j<count2.size();j++)
            if(count2[j]==count1[i])
                cout<<count1[i];
                
}

具体代码2(使用方法3)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>

using namespace std;

int get(string s, int b)  // 将b进制的数转化成十进制
{
    int res = 0;
    // 秦九韶算法
    for (auto &c: s)
        res = res * b + c - '0';
    return res;
}

int main()
{
    string a, b;
    cin >> a >> b;
    unordered_set<int> S;	//设置哈希表

    for (auto& c: a)
    {
        c ^= 1;
        S.insert(get(a, 2));	//将十进制插入哈希表
        c ^= 1;
    }

    for (auto& c: b)
    {
        char t = c;
        for (int i = 0; i < 3; i ++ )
            if (i + '0' != t)
            {
                c = i + '0';	//修改c,b中对应位也会修改
                int x = get(b, 3);
                if (S.count(x))	//判断该十进制是否在哈希表中
                {
                    cout << x << endl;
                    return 0;
                }
            }
        c = t;
    }
    return 0;
}

对于类似两集合匹配问题,都可以采用哈希表保存一个集合,在通过count函数判断另一集合的元素是否在第一个集合中存在。


2041.干草堆


一、问题描述

贝茜对她最近在农场周围造成的一切恶作剧感到抱歉,她同意帮助农夫约翰把一批新到的干草捆堆起来。开始时,共有 N个空干草堆,编号 1∼N。约翰给贝茜下达了 K个指令,每条指令的格式为 A B,这意味着贝茜要在 A…B范围内的每个干草堆的顶部添加一个新的干草捆。

例如,如果贝茜收到指令 10 13,则她应在干草堆 10,11,12,13中各添加一个干草捆。

在贝茜完成了所有指令后,约翰想知道 N个干草堆的中值高度——也就是说,如果干草堆按照高度从小到大排列,位于中间的干草堆的高度。

方便起见,N 一定是奇数,所以中间堆是唯一的。

请帮助贝茜确定约翰问题的答案。

输入格式:
第一行包含 N和 K。接下来 K 行,每行包含两个整数 A,B,用来描述一个指令。

输出格式:
输出完成所有指令后,N个干草堆的中值高度。

数据范围:
1≤N≤10^6,
1≤K≤25000,
1≤A≤B≤N

输入样例:
7 4
5 5
2 4
4 6
3 5

输出样例:
1

样例解释:
贝茜完成所有指令后,各堆高度为 0,1,2,3,3,1,0。将各高度从小到大排序后,得到 0,0,1,1,2,3,3,位于中间的是 1。


二、分析与代码

分析
这是很典型的一道用差分方法解决的问题,当然也可以用暴力法直接求,但是该题数据范围体现了暴力法时间复杂度达到了10^10,会超时,所以最好使用差分法。

难点
使用差分

方法
使用差分

具体代码

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

int main()
{
    int N,K;
    cin>>N>>K;
    int grass[N];
    for(int i=0;i<N;i++)
        grass[i]=0;
    int first,last;
    while(K--)
    {
        cin>>first>>last;
        grass[first-1]++;
        grass[last]--;
    }
    for(int i=1;i<N;i++)
    grass[i]=grass[i]+grass[i-1];
    sort(grass,grass+N);
    cout<<grass[N/2];
    return 0;
}

类似对数组中一块连续的内容进行相同的操作时可以优先考虑使用差分法。


2060.奶牛选美


一、问题描述

AcWing-2060.奶牛选美

二、分析与代码

分析
要找到两块斑点之间最近的距离:
1.将两块斑点的所有点坐标分开存储
2.使用暴力法分别求出保存的两块斑点的任意两点的距离
3.输出最小的距离

难点
1.如何将两块斑点区分开
2.如何将两块斑点的坐标分别存储

方法
1.可以使用深度优先搜索将两块斑点区分开,具体如下:找到一块斑点的其中一个位置a,并分别对该位置a的上下左右相邻位置b进行判断是否为斑点,若b为斑点,则再对b的上下左右相邻位置进行判断。
2.坐标由两位数组成,可以使用pair对其进行存储,在使用vector定义一个数组,数组类型为pair,数组大小为2,分别存储两块斑点的坐标。

具体代码

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

int N,M;
char crow[50][50];
typedef pair<int,int> PII;
vector <PII> niu[2];
int dx[]={-1,0,1,0},dy[]={0,-1,0,1};
//类似找坐标相邻坐标都可使用此方法
void dfs(int x,int y,int k)
{
    crow[x][y]='.';		//防止重复搜索
    niu[k].push_back({x,y});
    int a,b;
    for(int i=0;i<4;i++)
    {
        a=x+dx[i];
        b=y+dy[i];
        if(a>=0&&a<N&&b>=0&&b<M&&crow[a][b]=='X')
        {
            dfs(a,b,k);
        }
    }
}

int main()
{
    cin>>N>>M;
    for(int i=0;i<N;i++)
        cin>>crow[i];
    int k=0;
    for(int i=0;i<N;i++)
        for(int j=0;j<M;j++)
        {
            if(crow[i][j]=='X')
            dfs(i,j,k++);
        }
    int res=1e8;
    for(auto& a:niu[0])
        for(auto& b:niu[1])
        res=min(res,abs(a.first-b.first)+abs(b.second-a.second)-1); 
    cout<<res;
}

2019.拖拉机


一、问题描述

AcWing-2019.拖拉机

二、分析与代码

分析
此题可以转换为一个求单源最短路径的问题:
无干草的位置对应权值为0;有干草的位置对应权值为1。最后求拖拉机回到原点的最短路径值。该题路径权值不同,故使用Dijkstra算法求得最短路径。

难点
普通Dijkstra算法每次进行贪心选择此时可选择的最短路径时,一般都是将所有可选择路径存储在一个数组中,将该数组遍历一遍,再选出权值最短的路径;

优化:将所有可选择路径权值按小堆排序,每次选择堆头所对应的路径,可减少选择最短路径的时间,此方法称为Dijkstra算法的堆优化。

关键点:该题只有权值为0与1两种情况,故在选择最短路径时可以使用双端队列。

方法
使用双端队列,将可选择的权值为0的点存储在队头,权值为1的点存储在队尾,并且每次都选择队头点为权值最小点。

具体代码

#include <iostream>
#include <deque>
#include <cstring>

using namespace std;

typedef pair<int,int> PII;
const int N=1010;
bool g[N][N]={false},b[N][N]={false};   //g记录该点是否有干草,b记录该点是否走过
int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};

int bfs(int x,int y)
{
    int dist[N][N];
    deque <PII> q;
    memset(dist, 0x3f, sizeof dist);
    dist[x][y]=0;
    q.push_back({x,y});
    while(q.size())
    {
        auto l=q.front();
        q.pop_front();
        x=l.first;
        y=l.second;
        if(b[x][y]) continue;           //如果该点走过
        else    b[x][y]=true;
        if(x==0&&y==0)                  //到达起点
            break;
        for(int i=0;i<4;i++)
        {
            int a=dx[i]+x;
            int b=dy[i]+y;
            if(a>=0&&a<N&&b>=0&&b<N)
            {
                int w=0;
                if(g[a][b]) w=1;        //如果有干草
                if(dist[a][b]>dist[x][y]+w)
                    dist[a][b]=dist[x][y]+w;
                if(w) q.push_back({a,b});   //有干草,存储在队尾
                else q.push_front({a,b});   //无干草,存储在队头
            }
        }
    }
    return dist[0][0];
}

int main()
{
    int n,x,y;
    cin>>n>>x>>y;
    while(n--)
    {
        int first,last;
        cin>>first>>last;
        g[first][last]=true;
    }
    cout<<bfs(x,y);
}

2014.岛


一、问题描述

AcWing-2014.岛

二、分析与代码

分析
1.对于相邻同高的岛可以直接当做一个岛来处理
2.对一个岛分析有三种情况:
(1)左边岛与右边岛都比该岛高,淹没该岛后岛的数量加1
(2)左边岛与右边岛都比该岛低,淹没该岛后岛的数量减1
(3)左边岛与右边岛比该岛一个高一个低,淹没该岛后岛的数量不变
3.淹没的岛屿的高度是由低到高

难点
使淹没的岛屿的高度是由低到高,必须对岛屿按高度排序。此时如何将某高度的岛屿与其在实际位置对应起来?

方法
使用pair存储,具体如下:
1.先将岛屿信息存储到数组a中
2.再使用pair<int,int>,pair.first存储岛屿高度,pair.second存储岛屿的实际位置下标
3.按高度对pair排序,即对pair.first进行排序,则可将岛屿高度(pair.first)与岛屿实际位置下标(pair.second)对应起来,再通过下标到数组a中判断该岛的左右岛的高度属于哪种情况。

具体代码

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N=100010;
int h[N];
typedef pair<int,int> PII; 
PII g[N];

int main()
{
    h[0]=0;
    int n,k;
    cin>>n;
    int i=1;
    while(n--)              
    {
        cin>>h[i];
        g[i].first=h[i];
        g[i].second=i;
        if(h[i]==h[i-1])            //将相邻同高的岛当作一个岛存储
            i--;
        i++;
    }
    k=i;
    h[k]=0;
    g[k].first=h[k];
    g[k].second=k;
    sort(g+1,g+k);                  //对pair按岛屿高度排序
    int cns=1,res=1;
    for(i=1;i<k;i++)                //岛屿高度由低到高判断
    {
        int y=g[i].second;          //得到下标
        if(h[y-1]>h[y]&&h[y+1]>h[y]) cns++;
        if(h[y-1]<=h[y]&&h[y+1]<=h[y])  cns--;
        
        //如果该岛高度与某不相邻的岛同高,那么得将所有同高的岛判断一遍的最后结果再更新
        if(g[i].first!=g[i+1].first)    
            res=max(res,cns);
    }
    cout<<res;
}


一、问题描述

AcWing-

二、分析与代码

分析

难点

方法

具体代码



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值