程序员进阶之算法练习(四十四)

2023-09-21 19:25:03 浏览数 (1)

正文

题目1

题目链接 题目大意: 给出整数x,求两个整数a和b,满足: ???(?,?) ???(?,?)=? . GCD是最大公约数; LCM是最小公约数;

题目保证a和b存在;

输入: 第一行整数t,表示样例个数; (1≤?≤100) 接下来t个样例,每个样例一行,整数x;(2≤?≤10^9)

输出: 整数a和b; (要求范围,1≤?,?≤10^9)

Examples input 2 2 14 output 1 1 6 4

题目解析: 构造题,这里提供一种方法:1 (x-1)。

构造题,想到就简单;想不到就很难。 如果不能马上想到规律,可以尝试将数字设置小一些,比如说x=10; 再多考虑x=11、x=12这些,探索规律,然后将x=100进行验证。

代码语言:javascript复制
int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        cout << 1 << " " << n - 1 << endl;
    }
题目2 CopyCopyCopyCopyCopy

题目链接 题目大意: 给出n个整数的数组,现在将数组复制,放在数组末尾,重复n次; 求新的数组中,最长严格递增子序列的长度是多少?

输入: 第一行整数t,表示样例个数; (1≤?≤100) 接下来t个样例,每个样例2行; 第一行,整数? (1≤?≤10^5) 第二行,n个整数?1, ?2, …, ?? (1≤??≤10^9) 每个样例一行,表示最长递增子序列的长度;

Examples input 2 3 3 2 1 6 3 1 4 1 5 9 output 3 5 样例解释,3,2,1重复3次,得到[3,2,1,3,2,1,3,2,1] 最长递增子序列是[1,2,3],长度是3;

题目解析: 如果n个数字不相同,重复n次之后,必然可以选出n个数字,最长递增子序列的长度是n;(因为重复n次,每次选1个数字就好) 如果n个数字中存在相同,则相同的数字只能取一个; 所以答案就是数组中,数字不同的个数。

转换思路,题目变成求数组中不同数字的数量。

代码语言:javascript复制
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        
        int ans = 0;
        map<int, int> mp;
        for (int i = 0; i < n;   i) {
            int k;
            cin >> k;
            if (!mp[k]) {
                mp[k] = 1;
                  ans;
            }
        }
        cout << ans << endl;
    }
题目3 Numbers Exchange

题目链接 题目大意: 小明喜欢一种字符串Zebras,Zebras的定义是以0开头,以0结束,中间0/1交替出现,比如说:0, 010, 01010; 类似1, 0110, 0101这种就不是Zebras; 现在给出一串长度为n的字符串(全部是01组成); 小明希望把字符串分成若干个子序列,并且每个子序列都是Zebras;(每个字符存在子序列中,且只在一个) 如果不能,则输出-1;

输入数据: 一串字符串,长度不超过20w;

输出数据: 第一行,数字k,表示分为k个子序列;

Examples input 0010100

output 3 3 1 3 4 3 2 5 6 1 7 样例解释: 分3个子序列010,010,0;

题目解析: 由贪心知道,01010..的序列越长越好;(0的利用率提升) 题目的要求是每个字符都要放到子序列里去,那么有一种简单的做法: 每次从左到右扫描,在未选择的字符中,寻找最长的Zebras,比如说对样例0010100处理: 第一次扫描,扫出01010;(1,3,4,5,6) 第二次扫描,扫出0;(2) 第三次扫描,扫出0;(7)

但是这样的代价比较高,是N^2级别复杂度;(对于00001111000这样的数据) 考虑这样一种优化方案: 有两个队列x、y,队列x是0结束的子序列,队列y是1结束的子序列;

当遇到0时,从队列y取一个子序列,末尾拼0然后放入队列x;如果队列y没有数据,则新建一个子序列(因为0可以是单独的序列),然后将子序列放入队列x; 当遇到1时,从队列x取一个子序列,末尾拼1然后放入队列y;如果队列x没有数据,则无解;

这样每次都是O(1)的操作,总体的复杂度是O(N);

代码语言:javascript复制
    cin >> str;
    size_t n = strlen(str);
    
    queue<int> q[2];
    int id = 0;
    int ok = 1;
    for (int i = 0; i < n && (ok>0);   i) {
        if (str[i] == '0') {
            if (q[1].empty()) {
                ans[id].push_back(i);
                q[0].push(id  );
            }
            else {
                int x = q[1].front();
                q[1].pop();
                ans[x].push_back(i);
                q[0].push(x);
            }
        }
        else {
            if (q[0].empty()) {
                ok = -1;
            }
            else {
                int x = q[0].front();
                q[0].pop();
                ans[x].push_back(i);
                q[1].push(x);
            }
        }
    }
    if (ok > 0 && q[1].size() == 0) {
        cout << id << endl;
        for (int i = 0; i < id;   i) {
            cout << ans[i].size() << " ";
            for (int j = 0; j < ans[i].size();   j) {
                cout << ans[i][j]   1 << " ";
            }
            cout << endl;
        }
    }
    else {
        cout << -1 << endl;
    }
题目4 A Leapfrog in the Array

题目链接 题目大意: 有若干个格子排成一行序号是1、2、3、4、5、、、,有1~n的n个整数分别放在格子里,第i个数字放在第2i-1格子; 现在对数字进行操作: 每次把最右边的数字x,放在x左边最接近的空格里; 不断重复,直到x左边没有空的格子;

现在有q个询问,每个询问是数字x[i],求第x[i]个格子上面的数字;

输入数据: n and q (1 ≤ n ≤ 1e18, 1 ≤ q ≤ 200 000) 接下来q行,每行一个数字x[i];

输出数据: 对于每个询问,输出第x[i]个格子上面的数字;

Examples input 4 3 2 3 4 output 3 2 4

样例解释,见上图。

题目解析: 从题目的操作结果来看,最后操作停止的结果必然是n个数字分布在第1到n个格子里; 容易知道,小于等于n/2的数字是不会动的。(因为数字的左边没有格子) 相当于后面(x - n/2)数字排列紧密(没有空格),然后数字的最后一个跳到前面; 于是可以用递归的思想解决问题,不断重复,直到数字剩下1个。

思考?: 另一种解法: 我们用0表示空格,当n=8的时候,原序列为1,0,2,0,3,0,4,0,5,0,6,0,7,0,8 把每次移动的数字写出来: 8,8,7,8,6,7,5,8,4,6,3,7,2,5,1。 或许看不出来问题? 试试这么看: 8, 8,7 8,6,7,5, 8,4,6,3,7,2,5,1 每次都是从n-2^(i-1)开始,按顺序插入上一行的数字。 从题意知道,每次都会填补数字最左边的一个空格。 又根据移动的性质,我们知道填补第n个格子数字不会再移动。(因为当第n个格子成为最右边的数字时,左边已经没有格子) 所以只需要算出n个数中间的格子是倒数第k个空格,从上面的序列选择第k个数字,就是n个数字移动后最右边一个数字。

代码语言:javascript复制
/**
 1 2 3 4 5 6 按照原来的规则进行选择,最右边的一个

 @param n 前n个
 @return 最右边的一个
 */
lld solve(lld n) {
    if (n % 2) {
        return (n   1) / 2;
    }
    return n / 2   solve(n - n / 2);
}

int main(int argc, const char * argv[]) {
    // insert code here...
    lld n, q;
    cin >> n >> q;
    while (q--) {
        lld x;
        cin >> x;

        if (x % 2 == 1) {
            cout << (x   1) / 2 << endl;
        }
        else {
            cout << solve(n - x / 2)   x / 2 << endl;
        }
    }
    
    
    return 0;
}

总结

题目1 是简单构造题,构造题也有一些思考门路,从小样例开始分析规律; 题目2 是考察思路,代码很简单,核心在于有没有想到题目其实就是求数组的不同数字; 题目3 是考察实现能力,可能方法会有很多,但是需要找一个简单快捷方案,并且能输出答案; 题目4 看似很复杂,其实考虑下n=3、n=4、n=5,模拟下操作可以发现规律。

0 人点赞