前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >程序员进阶之算法练习(六十三)

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

作者头像
落影
发布于 2022-09-02 03:59:47
发布于 2022-09-02 03:59:47
25000
代码可运行
举报
文章被收录于专栏:落影的专栏落影的专栏
运行总次数:0
代码可运行

正文

题目1

题目链接 题目大意: 给出一个整数a[1],假设有下面这样一个迭代公式:

按照上面的规则,迭代k次之后,得到a[k]; 现在给出a[1]和k,求最终迭代出来的a[k]值是多少?

输入: 第一行,整数𝑡表示有t个样例数量 (1≤𝑡≤1000) 接下来每个样例一行,2个整数 𝑎1 and 𝐾 (1≤𝑎1≤1e18, 1≤𝐾≤1e16)

输出: 每个样例一行,输出a[k];

Examples input 8 1 4 487 1 487 2 487 3 487 4 487 5 487 6 487 7

output 42 487 519 528 544 564 588 628

题目解析: 题目咋一看,会很复杂;同时数字的范围特别大,又也没有什么明显规律。

这种时候,先尝试的模拟算一下,当我们多重复几次,直到x·y=0的时候,我们发现这个数字会停滞不动; 那么我们可以模拟这个过程,直到有数字为0。

那么有没有可能出现,x·y永远不为零呢?答案是不可能的,因为会进位。 数字的百位,最终总是会归零,并且不会需要很多次。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
lld calc(lld a) {
    lld x = 0, y = 9;
    while (a) {
        x = max(x, a%10);
        y = min(y, a%10);
        a /= 10;
    }
    return x*y;
}


int main(int argc, const char * argv[]) {
    // insert code here...
    
    int t;
    cin >> t;
    while (t--) {
        lld a, k;
        cin >> a >> k;
        while (--k) {
            lld tmp = calc(a);
            a += tmp;
            if (tmp == 0) {
                break;
            }
        }
        cout << a << endl;
    }   
    
    return 0;
}

题目2

题目链接 题目大意: 在数轴上,有一个点A放在x=n的位置上,现在想找到一个点B,B的所在位置是一个整数,并且满足: 原点O到B的距离 和 A到B的距离 两者的差为k;

有时候该位置不存在,我们可以每次把A的位置加1,现在想知道A最少需要加几次1,使得点B存在;

输入: 第一行整数 𝑡,表示t个样例 (1≤𝑡≤6000) 每个样例一行,整数𝑛 and 𝑘 (0≤𝑛,𝑘≤1e6)

输出: 每个样例一行,输出最少都要A点移动次数。

Examples input 6 4 0 5 8 0 1000000 0 0 1 0 1000000 1000000 output 0 3 1000000 0 1 0

题目解析: 假如B没有整数的要求,那么当n>=k的时候,则B肯定存在于O到A之间;n<k的时候,则A的位置不断加1,直到n=k; 当增加B的位置为整数限制时,即使n>=k(比如说n=1,k=0),由于B的位置是0.5,此时只能将A的位置加1,使得A=2; 考虑有哪些情况会出现B的位置为整数,我们可以发现是n为奇数k为偶数 和 n为偶数k为奇数两种情况,我们用 (n+k) % 2 可以区分; 至于n<k的情况,我们移动A点,使得n=k,将点B放在原点即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main(int argc, const char * argv[]) {
    // insert code here...
    
    int t;
    cin >> t;
    while (t--) {
        lld a, k;
        cin >> a >> k;
        if (a >= k) {
            cout << (a+k)%2 << endl;
        }
        else {
            cout << (k-a) << endl;
        }
    }   
    
    return 0;
}

题目3

题目链接 题目大意: ALICE和BOB在玩一个游戏,给出一个长度为n的回文字符串,由字符0和1组成; 两个人轮流行动,ALICE先行动,每次有两个选择: 1、花费代价1,将字符串某个字符0变成1; 2、翻转(reverse)字符串,要求是该字符串不是回文串,并且上一步操作不是翻转; 等整个字符串都是1的时候,游戏结束,花费代价较少的人获胜;

输入: 第一行是整数t,表示有t个样例 (1≤𝑡≤1e3) 每个样例2行,第一行是整数 𝑛 (1≤𝑛≤1e3). 第二行是01字符串; 输出: 每个样例一行,输出胜者名字(ALICE和BOB);如果平局则输出(DRAW);

Examples input 2 4 1001 1 0 output BOB BOB

题目解析: 这里有一个简单策略:对于一个回文串(最中间的数字不为0),那么可以采取A填哪个位置,B就填回文串对应的位置; 直到剩下2个0的时候,A填任何一个位置,B选择reverse;此时A只能再填一次0,B必胜;

先手者,除非回文串中间是0(当有空格的部分大于1时),否则必输;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Solution {
   static const int N = 100010;
public:
   int n, m;
   string str;
public:
   void solve() {
       int t;
       cin >> t;
       while (t--) {
           cin >> n;
           cin >> str;
           int cnt = 0;
           for (int i = 0; i < n; ++i) {
               if (str[i] == '0') {
                   ++cnt;
               }
           }
           if (cnt == 0) {
               cout << "DRAW" << endl;
           }
           else if (cnt == 1) {
               cout << "BOB" << endl;
           }
           else if (cnt % 2) {
               cout << (str[(n - 1) / 2] == '0' ? "ALICE" : "BOB") << endl;
           }
           else {
               cout << "BOB" << endl;
           }
       }
   }
}
ac;

题目4

题目链接 题目大意: ALICE和BOB在玩一个游戏,给出一个长度为n的字符串,由字符0和1组成; 两个人轮流行动,ALICE先行动,每次有两个选择: 1、花费代价1,将字符串某个字符0变成1; 2、翻转(reverse)字符串,要求是该字符串不是回文串,并且上一步操作不是翻转; 等整个字符串都是1的时候,游戏结束,花费代价较少的人获胜;

输入: 第一行是整数t,表示有t个样例 (1≤𝑡≤1e3) 每个样例2行,第一行是整数 𝑛 (1≤𝑛≤1e3). 第二行是01字符串; 输出: 每个样例一行,输出胜者名字(ALICE和BOB);如果平局则输出(DRAW);

Examples input 3 3 110 2 00 4 1010 output ALICE BOB ALICE

题目解析: 这里有一个简单策略:对于一个回文串(最中间的数字不为0),那么可以采取A填哪个位置,B就填回文串对应的位置; 直到剩下2个0的时候,A填任何一个位置,B选择reverse;此时A只能再填一次0,B必胜; 先手者,除非回文串中间是0(当有空格的部分大于1时),否则必输。

注意这个题目中,先手者是可以reverse操作的,这是一个比较大的优势; 如果起手是回文串,那么可以用上面的逻辑。(这里有一个很重要的点,先后手最大的差距是2,就是1001这种情况,因为都会采用最佳策略)

为了方便思考,我们引入函数f,f(str)表示回文串str,先手会赢多少; f(1001)=-2; f(101)=-1; (仅有一个0的情况下) f(10001)=1; 注意,除了没有0,平局的情况是不存在的。 我们知道回文串的胜负,有两种结果:-2/-1和1。

假如字符串没有任何位置可以操作1次,之后变成回文串,那么先手者Alice可以reverse,等待后手者操作后,依旧可以执行reverse;(这种策略可以占领1的优势,因为后手在一直补齐) 直到存在一个操作,可以对某个位置k进行操作,并且操作之后字符串变成回文串strK; 1、假如f(strK)=1,则Alice可以继续执行reverse操作,因为bob如果将字符串操作为strK,对Alice是有收益的; 2、假如f(strK)=-1,则Alice继续执行reverse会平局,因为bob花费1的代价让让alice面临-1的情况; (是否平局,取决于在达到下一步操作就能生成回文串的操作步数) 3、假如f(strK)=-2,则Alice必胜,Alice花费1的代价可以让对手面临-2的情况;

这里可以推出来,先手者必然不会输。(注意,这个前提是有2个0以上)

所以问题的关键是在于上面的情况2,怎么快速找到平局的case。

我们知道这个追赶的步数,应该等于字符串中1和0按照回文串要求不对应的数量。 比如说10011=>其中的str[1]和str[3]不一样,需要追赶; 那么统计一下,这个数量,即可得到追赶的步数step。

假如step=1,则刚好和构成回文串strNew之后,f(strNew)的-1收益持平,此时会平局。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Solution {
   static const int N = 100010;
public:
   int n, m;
   string pubStr;
   
   bool isPalindrome(string &str) {
       bool isPalindrome = true;
       for (int i = 0; i < n / 2; ++i) {
           isPalindrome = isPalindrome && (str[i] == str[n - i - 1]);
       }
       return isPalindrome;
   }
   
   int getCount(string &str) {
       int ret = 0;
       int cnt = 0;
       for (int i = 0; i < n; ++i) {
           if (str[i] == '0') {
               ++cnt;
           }
       }
       if (cnt == 0) {
           ret = cnt;
       }
       else if (cnt == 1) {
           ret= cnt;
       }
       else if (cnt % 2) {
           ret = (str[(n - 1) / 2] == '0' ? -2 : 1);
       }
       else {
           ret = -2;
       }
       return ret;
   }
   
public:
   void solve() {
       int t;
       cin >> t;
       while (t--) {
           cin >> n;
           cin >> pubStr;
           
           int cnt = 0, step = 0;
           for (int i = 0; i < n; ++i) {
               if (pubStr[i] == '0') {
                   ++cnt;
               }
               if (i < n / 2 && pubStr[i] != pubStr[n - i - 1]) {
                   ++step;
               }
           }
           if (cnt == 0) {
               cout << "DRAW" << endl;
           }
           else if (cnt == 1) {
               if (isPalindrome(pubStr)) {
                   cout << "BOB" << endl;
               }
               else {
                   cout << "ALICE" << endl;
               }
           }
           else {
               if (isPalindrome(pubStr)) {
                   if (cnt % 2) {
                       cout << (pubStr[(n - 1) / 2] == '0' ? "ALICE" : "BOB") << endl;
                   }
                   else {
                       cout << "BOB" << endl;
                   }
               }
               else {
                   if (step == 1 && cnt == 2) {
                       cout << "DRAW" << endl;
                   }
                   else {
                       cout << "ALICE" << endl;
                   }
               }
           }
           
       }
   }
}
ac;

题目5

题目链接 题目大意: 有n个整数的数组,对于一个数组的weight,定位数组中所有的[i, j],有多少个满足a[i]==a[j]; 问数组的所有子数组中,所有的weight有多少。

输入: 第一行是整数t,表示有t个样例(1≤𝑡≤1e4). 每个样例一行,第一行是整数n(1≤𝑛≤1e9). 输出: 每个样例一行,输出1到n的整数中,有多少个由相同数字组成的数。

Examples input 6 1 2 3 4 5 100

output 1 2 3 4 5 18

题目解析: 以数字[1,1,2,2,1,1]为例,看看中间[2,2]有多少种情况: 向左边延伸,左边起点有[2, x] [1,2,x] [1,1,2,x]三种可能; 向右边延伸,右边结尾有[x, 2] [x, 2,1] [x,2,1,1]三种可能; 包括[2,2]的所有子数组,即是从左边起点选择一个起点(3种可能),从右边结尾选择一个结点(3种可能,一共有3x3=9种可能。 容易知道,对于[i, j],假如有a[i]==a[j],则一共会有i(n-j+1)的可能。(i下标从1开始) 我们用left[i]来表示i,right[i]来表示n-i+1,则上面的公式表示为left[i]right[j]。

当有[i,j,k] 满足 a[i] == a[j] == a[k]的时候,我们有left[i]right[j]+left[i]right[j] + left[j]*right[k]。

所分组累计下,用map区分;然后逐个计算下即可。

注意超long long的trick。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Solution {
    static const int N = 100010;
public:
    int n, m;
    int a[N];
    map<int, vector<int>> h;
    
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            cin >> n;
            h.clear();
            for (int i = 0; i < n; ++i) {
                cin >> a[i];
            }
            for (int i = 0; i < n; ++i) {
                if (h.find(a[i]) != h.end()) {
                    h[a[i]].push_back(i + 1);
                }
                else {
                    vector<int> tmp;
                    tmp.push_back(i + 1);
                    h[a[i]] = tmp;
                }
            }
            lld ans = 0;
            for (map<int, vector<int> > :: iterator it = h.begin(); it != h.end(); ++it) {
                vector<int> vec = it->second;
                lld sum = 0;
                for (int i = 0; i < vec.size(); ++i) {
                    sum += (n - vec[i] + 1);
                }
                for (int i = 0; i < vec.size(); ++i) {
                    sum -= (n - vec[i] + 1);
                    ans += 1LL * vec[i] * sum;
                }
            }
            cout << ans << endl;
        }
    }
}
ac;
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-07-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
程序员进阶之算法练习(九十五)
题目链接 题目大意: 有n个整数的数组a,现在需要构造一个1到n的排列b; 令𝑐𝑖 = 𝑎𝑖−𝑏𝑖,希望最终𝑐𝑖 出现尽可能多的不同整数。
落影
2024/01/06
1320
程序员进阶之算法练习(九十五)
程序员进阶之算法练习(八十三)
题目链接 题目大意: 有长度为n的整数数组a,数组元素都由-1和1组成; 现在每次可以选择一个数组位置,翻转位置上元素(-1变成1,1变成-1); 假如想要实现下面的要求,最少需要多少次操作: 𝑎1+𝑎2+…+𝑎𝑛≥0 𝑎1⋅𝑎2⋅…⋅𝑎𝑛=1
落影
2023/08/20
2470
程序员进阶之算法练习(七十二)
题目链接 题目大意: 给出一个字符串,由小写字母组成; 现在Alice和Bob在玩游戏,轮流从字符串中移除一个子串,Alice先操作; Alice允许移除偶数长度子串,Bob允许移除奇数长度子串;(也允许不移除) 最终看每个人移除子串的分数总和,字母a是1分,b是2分、、、z是26分; 问最终谁能赢得游戏,以及胜者领先的分数;
落影
2023/03/07
2700
程序员进阶之算法练习(九十九)
题目链接 题目大意: 有三个长度为n的字符串a、b、c,字符串都是小写字符; 有一个长度为n的模版t,模版会与字符串a、b、c匹配,匹配规则如下: 1、假如模版的字符为小写字母,则对应位置必须是相同字符才算匹配; 2、假如模版的字符为大写字母,则对应位置则不能是相同字符才算匹配; 比如说模板abc和字符串abc是匹配的,模板ABC和字符串def也是匹配的,模板ABC和字符串abc是不匹配的;
落影
2024/02/25
1450
程序员进阶之算法练习(九十三)
题目链接 题目大意: 有3个字符a/b/c,排成一行; 现在可以选择两个字符,交换对应的位置; 上述操作最多只能执行一次,问能否得到abc的顺序。
落影
2023/12/18
1510
程序员进阶之算法练习(八十一)
题目链接 题目大意: 给出n个整数的数组,现在可以对数组进行以下操作: 选择数组中任意两个不同的整数a[i]和a[j],令a[i]=x,a[j]=y,其中满足x*y = a[i] * a[j];
落影
2023/08/09
3590
程序员进阶之算法练习(九十七)
现在光标停留在最左边的数字1处,我们可以进行以下的操作: 1、将当前光标所在位置的数字输出; 2、移动光标到相邻的数字,比如说从1移动到2,从2移动到3;(1的左边不能移动,0的右边不能移动)
落影
2024/02/10
1160
程序员进阶之算法练习(九十七)
程序员进阶之算法练习(六十四)
题目链接 题目大意: 给出一个字符串(由26个大写字母组成),询问这个字符串中,是否相同的字母都连在一起。
落影
2022/09/08
2420
Codeforces Round #721 (Div. 2)
给你一个n,找到最大的k,使得n & (n-1) & (n-2) & ... & k == 0,其中& 表示按位与操作。
Here_SDUT
2022/08/08
2920
程序员进阶之算法练习(九十八)
题目链接 题目大意: 在一个国际象棋的棋盘上,有一个棋子,它的移动规则类似马,能够朝着横or竖方向移动距离a,然后朝竖or横(和之前不同)移动距离b; 比如说马的移动规则就是a=1,b=2;
落影
2024/02/18
1930
程序员进阶之算法练习(九十八)
程序员进阶之算法练习(八十七)
题目链接 题目大意: 给出一个整数的数组,长度为n; 现在可以进行以下的操作: 选择长度不小于2的区间[l, r],将区间内的整数依次进行异或操作,然后将得到的整数替换区间所有的数字;
落影
2023/10/18
1990
程序员进阶之算法练习(八十七)
程序员进阶之算法练习(六十二)AK练习
题目链接 题目大意: 小明有a个1元硬币,b个2元硬币; 小明想要购买一个商品,并且不想找零; 现在小明想知道自己无法给到最低价格是多少;
落影
2022/06/05
5480
程序员进阶之算法练习(六十)
题目链接 题目大意: 给出一个整数n,求一个最大整数满足: 1、整数各个数字加起来等于n; 2、没有两个相同的数字相邻; 3、数字中不包括0;
落影
2022/04/24
2140
程序员进阶之算法练习(六十)
程序员进阶之算法练习(七十三)
题目链接 题目大意: 有两种车分别有4个轮子和6个轮子,现在只知道若干个车的轮子总数,想知道最少和最多有几辆车;
落影
2023/03/07
3040
程序员进阶之算法练习(六十七)
题目链接 题目大意: 给出n个整数的数组a和b,现在可以执行任意次下面的操作: 选择索引x,交换数组a和b中的元素a[x]和b[x];
落影
2022/10/04
2430
程序员进阶之算法练习(八十八)- CF883
题目链接 题目大意: 在地面上有n个点,点都在同一条垂直地面的线上面,每个点的高度为a[i]; 每个点有一条绳子,绳子长度为b[i],一端连着点,一端连着小球(每个点相连的小球是同一个);
落影
2023/10/23
1810
程序员进阶之算法练习(八十八)- CF883
程序员进阶之算法练习(六十一)
题目链接 题目大意: 给出正整数n,求整数1到整数n之中,有多少整数是由单一的字符组成,比如说1 , 77, 777, 44 和 999999 就是符合要求的整数。 整数1到18中,只有 1, 2, 3, 4, 5, 6, 7, 8, 9 和 11符合要求。
落影
2022/05/13
2160
程序员进阶之算法练习(九十四)
题目链接 题目大意: 有n个整数组成的数组a,现在可以对数组a的元素任意打乱顺序,要求满足: 假设打乱后的数组是b,要满足:𝑏1+𝑏2=𝑏2+𝑏3=…=𝑏𝑛−1+𝑏𝑛=k 也就是相邻两个数字的和相同。
落影
2023/12/23
1080
程序员进阶之算法练习(九十四)
程序员进阶之算法练习(六十五)
题目链接 题目大意: 给出n个整数和整数x,问能否找到一个顺序: 按照这个顺序累加数字,中间不会出现数字和等于x; 已知n个整数互不相同。
落影
2022/09/23
1670
程序员进阶之算法练习(五十四)
题目链接 题目大意: 给出n个整数,还有一个数字d; 要求去除最少的数字,使得剩余数字的最大最小值差不大于d;
落影
2021/11/24
2620
程序员进阶之算法练习(五十四)
相关推荐
程序员进阶之算法练习(九十五)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验