【计算思维机试2】C.纠结之神

题目

众所周知,pengym 是一个动不动就开始纠结的人,或者说是纠结之神。

这代表着,他对于任何可以选择的事情都会不断的犹豫纠结。今天,他就遇到了这样一件让他纠结的事。

现在有一个长度为 n 的数组 A=[a1,a2,...,an] ,pengym 觉得这个数组现在这样的排列并不美观,于是他决定,对这个数组进行向左或向右的循环移位!

举例来说,现在有一个数组 A=[2,3,4,1,5]。pengym 可以选择对数组进行向左循环移位 22 个单位的操作,那么数组 A=[4,1,5,2,3],也就是所有数向左移动 2 步,若超出边界则直接移动到另一侧。

pengym 将整个数组按照上述方式,向左向右移来移去,怎么移都没能符合他的心意,纠结了很长一段时间后,他不想再移了,他想问问你,他所指定的那个位置上的数最后到底变成了多少?

请注意,每一次移动都是在上一次移动完成后再进行。

输入数据

第一行两个正整数 n 和 m,分别表示数组的长度和 pengym 对数组进行循环移位的操作次数。

第二行输入 n 个正整数,分别表示 a1,a2,...,an。

第三行开始往下 m 行,每行输入两个正整数 op 和 x,其中 op 表示本次操作是向左移动还是向右移动,𝑥x 表示移动的步数。当 op=0 时,表示向左移动;当 op=1 时,表示向右移动。

最后输入一个正整数 k,表示题干中所描述的指定位置。

输出数据

输出共一行,一个整数 ans,表示在经过多次的循环移位后 A 数组中第 k 个位置最终的值。

输入样例
5 2
2 3 8 1 5
0 2
0 4
4
输出样例
5
说明

对于 30% 的数据:1≤n,m,x≤100。

对于 100% 的数据:1≤n,m,x≤10^5,1≤k≤n,1≤ai≤10^9。

在样例中,pengym 对该数组进行了两次操作,首先,将 [2,3,8,1,5] 向左循环移动 2 步得到 [8,1,5,2,3],接着再向左移动了 4 步得到[3,8,1,5,2]。因此第 4 个位置是 5。

作者: pengym

思路

开悟前思路:

模拟题,按照题目描述写代码就行,难在移动数组时的循环条件设置/新下标问题,情况合并和其他一些细节。

数组可以分为两部分,一部分是移动后没有越界的:

        (左)新下标=原下标-步长;(右)新下标=原下标+步长

一部分是越界了的:

        (左)新下标=原下标-步长+数组长;(右)新下标=原下标+步长-数组长

开悟后思路:

突然一下子就懂了,感觉自己好蠢,数组移动不就相当于要读取的位置移动吗​​​​​​​。。。

[2,3,1,8,5]要读取第四个,数组左2+左4合并后相当于左1,数组左1相当于要读取的下标右1

所以思路应该是:​​​​​​​初始化数组->合并移动情况[步长对周期(数组长度)取模]->方向取反步长不变就是下标的移动情况->直接输出对应下标的元素

收获

1.关于中间过程的合并

数组移动的过程是可以合并的。比如样例中先向左移动2步,再向左移动4步,其实就可以合并成向左移动6步。

数组长度为5,向左移动的步数5的倍数就相当于数组没有移动,所以要取模把步数控制在周期内,6%5得1,向左移动6步等效于向左移动1步。

过程合并超级重要!!我开始没有意识到合并的问题,每一步都把中间过程产生的数组记录下,提交TLE超时了。很多题都是不用求中间变量的,直接一步到位得出结果即可。

2.​​​​​​​数组移动=​​​​​​​下标反方向移动

这点也很重要,操作下标的值比操作数组要简单太多了。

3.周期性

不管是数组还是读取下标的移动,只要步数是数组长的整数倍就相当于没有改变,所以要对数组长取模,得到一个周期的等效步长。

代码

方法一:移动数组
//方法一(按照题目要求移动数组)
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    int n, m,x,new_pos,kkk,tmp_direc,tmp_x;
    x=0;
    cin >> n >> m;
    vector<int> vec(n);
    vector<int> new_vec(n);
    //初始化数组
    for (int i = 0; i < n; i++) cin>>vec[i];
    //合并所有情况(左移为正)
    for (int i = 0; i < m; i++)
    {
        cin >> tmp_direc>> tmp_x;
        if (tmp_direc == 0)x -= tmp_x;
        else x += tmp_x;
    }
    cin>>kkk;
    if(x==0)            //x==0就是没移动
    {
        cout<<vec[kkk-1]<<endl;
        exit(0);
    }
    x %= n;
    //移动数组
    for (int j = 0; j < n; j++)
    {
        new_pos = j + x;
        if (new_pos >= n) new_pos -= n;
        else if(new_pos<0) new_pos += n;
        new_vec[new_pos] = vec[j];
    }
    cout << new_vec[kkk-1] << endl;
    return 0;
}
方法二:移动下标
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    int n, m,x,kkk,tmp_direc,tmp_x;
    x=0;
    cin >> n >> m;
    vector<int> vec(n);
    //初始化数组
    for (int i = 0; i < n; i++) cin>>vec[i];
    //合并所有情况(数组左移为正)
    for (int i = 0; i < m; i++)
    {
        cin >> tmp_direc>> tmp_x;
        if (tmp_direc == 0)x += tmp_x;
        else x -= tmp_x;
    }
    //读取下标,因为数组左移步长为正,所以kkk+=x就是kkk右移后的坐标
    cin>>kkk;
    x%=n;
    kkk-=1;
    kkk+=x;
    if(kkk<0)kkk+=n;            //下标越界后的处理
    else if(kkk>=n) kkk-=n;
    cout << vec[kkk] << endl;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值