一些经典的算法题目cpp

组队竞赛(贪心)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x953bAiv-1659875472037)(C:/Users/15795/Desktop/img/130f0970b4864511b40bcca1c1e1ab07.png)]

解题思路

本题的主要思路是贪心算法,贪心算法其实很简单,就是每次选值时都选当前能看到的局部最解忧,所
以这里的贪心就是保证每组的第二个 是次大的 ;
先排好序,每次使用最左边的和最右边的两个进行组队,可以保证每队中间位置局部最大,对其加和;

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

int main()
{
    vector<int>arr;
    int n,tmp;
    while(cin>>n)
    {
        while(cin>>tmp)
        {
            arr.push_back(tmp);
        }
        //录入完成;
       
        sort(arr.begin(),arr.end());
        long long ret = 0;
        for(int i = n;i<arr.size();i+=2)
        {
            ret+=arr[i];
        }
        cout<<ret<<endl;
        
    }
    
    return 0;
}

删除公共字符(哈希)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vwSoqQKe-1659875472039)(C:/Users/15795/Desktop/img/8767e62a781443809f8c0803e84e7589.png)]

解题思路

搞个哈希表,存入要删的字符,遍历原字符串,用哈希表过滤的不需要的即可;

#include<iostream>
#include<string.h>
#include<vector>
#include<algorithm>
#include<string>
#include<unordered_set>
using namespace std;

int main()
{
    string s1,s2;
    unordered_set<char>st;
    // 注意这里不能使用cin接收,因为cin遇到空格就结束了。
    // oj中IO输入字符串最好使用getline。
    getline(cin,s1);
    getline(cin,s2);

    for(int i = 0;i<s2.size();i++)
    {
        st.insert(s2[i]);
    }
    for(int i = 0;i<s1.size();i++)
    {
        if(st.find(s1[i])==st.end()) cout<<s1[i];
    }
    cout<<endl;
    return 0;
}

排序子序列(数组)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VPUJOUC8-1659875472039)(C:/Users/15795/Desktop/img/a7c482daf4ec4ddc9b8e86a4d1638170.png)]

解题思路

flag作为标志转换器1为递增序列,-1为递减序列,每次变换完恢复0(待设置状态),循环统计转折次数;

#include<iostream>
using namespace std;

int main()
{
    int n;
    cin >> n;
    int arr[n];
    //当第一次出现子序列转折时,其实已经"一刀两断",划分成两个子序列了
    int cnt = 1;
    int flag = 0;
    
    if (n <= 2) return 1;

    for (int i = 0; i < n; i++)
    {
        cin >> arr[i];
    }
    for (int i = 0; i < n; i++)
    {
        if (i + 1 < n && arr[i] < arr[i + 1])
        {
        	//flag待设置,根据递增还是递减设置flag;
            if (flag == 0) flag = 1;
			//flag-1递减状态,序列出现递增,划分,cnt++flag恢复初始待设置状态
            if (flag == -1)
            {
                flag = 0;
                cnt++;
            }
        }
        else if (i + 1 < n && arr[i] > arr[i + 1])
        {
            if (flag == 0) flag = -1;

            if (flag == 1)
            {
                cnt++;
                flag = 0;
            }
        }
        //else 这里就是arr[i] > arr[i + 1],不处理只需要让i++ for里面处理了;
    }
    cout << cnt << endl;
    return 0;
}

倒置字符串(字符串)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kmOE6ppT-1662027950413)(C:/Users/15795/Desktop/img/2dd7b5258249492ebd644991f712b4b3.png)]

解题思路

1.先反转每个单词;
2.再反转整体字符串则可以达到目的;(善于观察)

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

int main()
{
    string str;
    vector<int>arr;
    getline(cin,str);
    int len = str.size();
    //先反转每个单词
    int l = 0;
    int r = 1;
    while(l<len)
    {
        while(r<len&&str[r]!=' ') r++;//找出空格' 'or字符串结尾位置;(最后一个单词也要反转)
        reverse(str.begin()+l,str.begin()+r);
        l = r+1;
        r = l;
    }
    //再反转整体
    reverse(str.begin(),str.end());
    cout<<str<<endl;
    return 0;
}

数组中出现次数超过一半的数字(数组)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w76hcKWe-1659875472040)(C:/Users/15795/Desktop/img/9f40e5ca0f83440086c9292f70d352c0.png)]

解题思路

思路1:超过一半肯定mid位置是这个数,sort+返回;
时间O(nlogn)
思路2:出现次数抵消原理,遍历一遍最终一定剩下出现次数最多得数字,直接返回;
时间O(n),空间O(1)最优;

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int ret = numbers[0];//记录出现次数多得数字;
        int times = 0;//记录出现次数;
        for(auto &e:numbers)
        {
            if(e!=ret) times--;
            else times++;
            
            if(times == 0)
            {
                 ret = e;
                 times++;
            }
        }
        return ret;
    }
};

进制转换(数学)

在这里插入图片描述

解题思路

进制转换:辗转相除法;
不仅限于转二进制,有可能转16进制,因此用一个字符数组映射10-_>‘a’,11–>b’等;

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

int main()
{
    string ret;
    char arr[]= {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    int x,n;
    cin>>x>>n;
    //特殊情况,不然会打印空字符串;
    if(x == 0)
    {
        cout<<0<<endl;
        return 0;
    }
    //负数在开头先输出'-'代表其为负数,再将其按整数运行,因为arr映射的时候下标都是正的;
    if(x<1)
    {
         cout<<'-';
        x*=-1;
    }
    //辗转相除
    while(x)
    {
        ret+=arr[x%n];
        x/=n;
    }
    //每次添加入ret的结果是低位到高位的,最后需要翻转输出
    reverse(ret.begin(),ret.end());
  
    cout<<ret<<endl;
    return 0;
}

连续最大和(贪心,动态规划)

在这里插入图片描述

解题思路

1.贪心,依次累加,将出现过的最大值存入max,若sum变<0,则舍弃 置sum=新的arr[i];
2.dp; dp[i] = max(nums[i],nums[i]+dp[i-1]);//状态转移方程;

贪心

class Solution {
public:
//贪心;
    int maxSubArray(vector<int>& nums) {
        int ans = INT_MIN;
        int sum = 0;

        for(int i = 0;i<nums.size();i++)
        {
            //如果上次的sum为<0 则置换为nums[i];
            if(sum<=0)
            {
                sum = nums[i];
            }
            else
            {
                sum+=nums[i];
            }
            //每次更新cur;
            if(sum>ans)
            {
                ans = sum;
            }

        }
        
        return ans;
    }
};

DP

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        //dp数组dp[i] 就是以数组下标为 i 的数做为结尾的最大子序列和;
        int dp[n];
        //dp初始化
        dp[0] = nums[0];
        int ret = nums[0];
        
        for(int i = 1;i<n;i++)
        {
            dp[i] = max(nums[i],nums[i]+dp[i-1]);//状态转移方程;
            if(ret<dp[i]) ret = dp[i];
        }
        return ret;
    }
};

不要二(贪心,数学)

在这里插入图片描述

解题思路

所谓欧几里得距离,就是两点之间得直线距离;(距离L=开根号((x2-x1)^2+(y2-y1)^2))
题目要距离L不能等于2,则L^2!=4,意味着(x2-x1)^2+(y2-y1)^2!=4根据表达式两个整数平方的和为4能看出只有0+4,4+0,这两种情况(对应x2-x1=2或y2-y1=2);
则用贪心的思路,可以看出:假设放蛋糕的位置是(x1,y1),则不能放蛋糕的位置(x2,y2),满足x1=x2,y1-y2=2或者x1-x2=2,y1=y2.)

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

int main()
{
    int N, M;
    cin >> N >> M;
    vector<vector<int>>arr;
    arr.resize(N);
    //将棋盘内全放上蛋糕
    for (int i = 0; i < N; i++) arr[i].resize(M, 1);
    int cnt = 0;
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < M; j++)
        {
            if (arr[i][j] == 1)//某个蛋糕的位置,其位置i+2 合 j+2不能放,去掉蛋糕;
            {
                cnt++;
                if (i + 2 < N)arr[i + 2][j] = 0;
                if (j + 2 < M)arr[i][j + 2] = 0;
            }
        }
    }
    cout << cnt << endl;
    return 0;
}

把字符串转换成整数(库函数atoi的实现)(类型转换,边界处理)

在这里插入图片描述

解题思路

本题关键是字符串可能100长度,那么显然会越界int~[-INT_MAX+1,INT_MAX了],atoi是直接取界返回的;
用一个大于int范围得long long ret依次进行字符转数字的处理,一但越界就停止转换,直接返回边界,牵扯类型转换;

class Solution {
public:
    //类似于库函数stoi的实现;
    //要点 int范围~[-INT_MAX+1,INT_MAX],越过了需要令其等于边界;
    int StrToInt(string str) {
        int flag = 1;
        if(str[0]=='-') flag = -1;//判断负数;
        int len = str.size();
        long long ret = 0;//数组大小100 因此某个int可能越界,搞个大的LL来处理;
        for(int i = 0;i<len;i++)
        {
            char tmp = str[i];
            if(!isdigit(tmp))
            {
                if(tmp=='+'||tmp=='-') continue;
                return 0;//出现非法字符题return 0库里return ret(非法字符前面
            }
            
            ret = ret*10+(tmp-'0');//字符串从头到尾转换成数字的核心逻辑
            
            if(flag==1&&ret>INT_MAX)//正数ret某次处理完>INT_MAX了
            {
                ret = INT_MAX;
                break;
            }
            if(flag==-1&&ret>static_cast<long long>(INT_MAX)+1)//负数ret某次处理完>INT_MAX+1了,这里必须提升一下整形INT_MAX的范围再+1,否则会溢出!!!
            {
                ret = INT_MAX+1;
                break;
            } 
        }
        //最后LL强转成int返回;

        return flag==1?static_cast<int>(ret):static_cast<int>(-1*ret);
    }
};

另类加法(数学,位运算)

在这里插入图片描述

解题思路

不能用±*/;那就用位运算;
两个数求和,其实就是求和后当前位的数据+两个数求和的进位;
求和后当前位的数据a^b(两数对应位置0,0->0;1,1->0;0,1->1)
求和的进位:(a&b)<<1(因为a&b找到求和前对应的位置都为1,所以求和后该位置应该置0进1位了)

class UnusualAdd {
public:
    int addAB(int A, int B) {
        //可能连续进位eg(111+001),所以用while
        while(B)//无进位则退出;
        {
            int tmp = A&B;//找到需要置0进位的多个位置i;
            A = A^B;
            B = tmp<<1;//<<1,代表i位置进位1位后需要加的数,然后准备与原数继续相加;
        }
        return A;
    }
};

走方格的方案数(动态规划,路径)

在这里插入图片描述

解题思路

DP,从头开始记录到达每一个位置所拥有的最多种路径,逐步推演到终点;
题目规定只能向下或者向上走,dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
能到达第一行和第一列上的位置所有路径均为1;

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int n, m;
    cin >> n >> m;
    //初始化棋盘格;沿着边缘线走,则大小(n+1)*(m+1)
    n++;
    m++;
    int dp[n][m];
    memset(dp,0, n * m*4);
    //只能向左或者向下走,则第一行&&第一列就都只有一种走法;dp数组初始化;
    for (int i = 0; i < n; i++)
    {
        dp[i][0] = 1;
    }
    for (int i = 0; i < m; i++)
    {
        dp[0][i] = 1;
    }
    //动态规划思想;dp[i][j] = dp[i - 1][j] + dp[i][j - 1];dp[i][j]=到i,j位置最多的路条数;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            if (dp[i][j] == 0)
            {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
    }
    cout << dp[n - 1][m - 1] << endl;
    return 0;
}

最近公共祖先(二叉树)
在这里插入图片描述

解题思路1

上述连续整数二叉树中父节点与子节点的关系为:root=child/2;因此a,b自下向上寻找相同的root即可

class LCA {
public:
    int getLCA(int a, int b) {
        // write code here
        while(a!=b)
        {
            if(a<b) b/=2;//在下面的child先走,可以保证两边平衡找最近的根;
            else a/=2;
        }
        return a;
    }
};

解题思路2

左孩子:root2,右孩子:root2+1;在二进制表示为root的二进制序列尾部追加0或1;
因此某一个祖先root发展子代的时候只是不断追加0或1,则该祖先的二进制序列对于子代的序列是公共前缀;
那么本题从高位到低位找出a,b的最长公共前缀(离a,b最近的公共祖先)转换成十进制即可;

class LCA {
public:
    string _to_string(int x){
        string ans="";
        while(x){
            ans=(char)((x&1)+'0')+ans;//这个+运算顺序很巧妙地保证了转换后低高位一致的顺序行;
            x/=2;
        }
        return ans;
    }
    int getLCA(int a, int b) {
        string sa=int2b(a);
		string sb=int2b(b);
        int ans=0;
        for(int i=0;i<sa.length()&&i<sb.length();i++){
        //从高位到低位找最长公共前缀并转换成十进制
			if(sa[i]==sb[i]) ans=ans*2+(sa[i]-'0');
			else break;
		}
        return ans;
    }
};

参数解析(模拟,字符串)

在这里插入图片描述

解题思路

经典的模拟字符串处理问题;
但值得注意的是模拟问题的if else if else…的合理顺序和格式,回事代码的逻辑清晰度和可读性大大提高;
本题就两种状态;
1.普通分割,遇到‘ ’停止记录;
2.”str“两引号之间的分割,就算遇到’ '也要继续追加;出了这个”str“状态还是会有‘ ’将其正确停止并记录

#include<bits/stdc++.h>
using namespace std;
int main() {
    string a;
    getline(cin, a);
    vector<string> ans;
    bool flag = false;//状态转换机?
    int index = 0;
    string tmp = "";
    for (int i = 0; i < a.size(); i++) {
        if(a[i]=='"')//遇到‘”’,状态切换;
        {
            flag = !flag;
        }
        else if(a[i]==' '&&!flag)//普通状态,遇到' '需要记录清空tmp了
        {
            ans.push_back(tmp);
            tmp.clear();
        }
        else//要么普通状态没遇到“,要么特殊”输入“状态,都继续向后追加;
            tmp+=a[i];
    }
    if (tmp != "")//输出最后一个子段
        ans.push_back(tmp);
    cout << ans.size() << endl;
    for (int i = 0; i < ans.size(); i++)
        cout << ans[i] << endl;
}

跳石板(贪心,动态规划)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qHfNcALM-1659875472044)(C:/Users/15795/Desktop/img/9eda79405ca646ed8c79d8245b15b555.png)]

解题思路

题目要求计算从n~m位置最少的跳跃次数,那么如果能依次算出n到m每个位置的最少次数逐渐向后推演,第m个算出的数据就是答案;
int dp[m+1]数组记录走到i位置石板所需要的最少跳跃次数;
int dp[m+1]初始设为-1(代表没走到的位置),dp[n] = 0为起始位置;
arr中记录当前位置i的所有约数,在dp数组中依次假设推演(贪心);

  • 如果dp[i+arr[j]]==-1,代表该位置没到过,则dp[i+arr[j]] = dp[i]+1,代表从i位置一步到达i+arr[j]并记录;
  • 如果dp[i+arr[j]]!=-1,代表该位置到过,则dp[i+arr[j]] = min(dp[i]+1,dp[i+arr[j]]),代表从i位置一步到达i+arr[j]并记录;这也就是状态转移方程
#include<bits/stdc++.h>
using namespace std;
//求a的所有公约数,存入数组
void f(int v,vector<int>&a)
{
    for (int i = 2; i <= sqrt(v); ++i)//这里求约数的优化,O(n)->O(log2)大大提高了效率
    {
	   if (v % i == 0)
	   {
			a.push_back(i);
		    if (v / i != i) a.push_back(v / i);
	   }
    }
}
int main()
{
    int n,m;
    cin>>n>>m;
    int dp[m+1];//dp[i] == 走到i位置石板所需要的最少跳跃次数 
    memset(dp,-1,(m+1)*4);//-1代表当前不能达到;
    dp[n] = 0; //初始化,注意n是起点;
    for(int i = n;i<=m;i++)
    {
        if(dp[i]==-1) continue;//某个位置i都没到过,不需要考虑该i位置向后走了 直接跳过;
        //所有公约数数组;
        vector<int>arr;
        f(i,arr);
        sort(arr.begin(),arr.end());//这里把约数排序为了下面break优化越界约数(步长);
        //将arr种所有 有可能的公约数(步数)都考虑一边;
        for(int j = 0;j<arr.size();j++)
        {
            if(i+arr[j]>m) break;//某个约数(步长)越界了,break,后面的也超了
            else
            {
                if(dp[i+arr[j]]!=-1)//之前某一步来过
                {
                    //状态转移更新方程;
                    dp[i+arr[j]] = min(dp[i+arr[j]],dp[i]+1);
                }
                else dp[i+arr[j]] = dp[i]+1;//之前还没哪一步来过
             }
         }
            
    }
    
    cout<<dp[m]<<endl;
    return 0; 
}


幸运的袋子(dfs深度搜索+剪枝)

在这里插入图片描述

解题思路

对于任意两个正整数a,b如果满足 a+b>ab,则必有一个数为1,1很妙噢;
题目可以去除任意个小球,则说明这是一道灵活多答案组合,直接想想dfs深度搜索all in的出来不,发现貌似给数组排个序可以,适当剪枝,注意题目同数字小球算一种,那么别忘了去重;

#include<bits/stdc++.h>
using namespace std;
//dfs(~深搜)+剪枝;
int ans = 0;
int sum = 0;
int mul = 1;
int n;
int arr[1010];
void dfs(int index)
{
    for(int i = index;i<n;i++)
    {
        //加入 for循环的dfs精髓1for循环的dfs精髓1for循环的dfs精髓1for循环的dfs精髓1for循环的dfs精髓1for循环的dfs精髓1
        sum+=arr[i];
        mul*=arr[i];
        if(sum>mul)//满足条件 ans+1;
        {
            ans++;
            dfs(i+1);
         }
        else if(arr[i]==1) dfs(i+1);///sum>mul但是arr[i]==1,再往下dfs一次有可能满足,那时候ans再加;
        
        else 
        {
             //不满足条件,恢复+break
            sum-=arr[i];
            mul/=arr[i];
            break;//sum>mul&&arr[i]!=1,不满足 剪枝 跳出循环;
        }
        //恢复 for循环的dfs精髓2for循环的dfs精髓2for循环的dfs精髓2for循环的dfs精髓2for循环的dfs
        
        //if elseif else走完,相当于i位置后面的子dfs都搞定了,则恢复i位置+“DFS向后去重”;(dfs的深搜性质必须去冲,不然会dfs出多组一样的答案,可以用1 1 1 2这个组合模拟一下即可看出来)
        sum -= arr[i];
		mul /= arr[i];
		for (; i < n - 1 && arr[i] == arr[i + 1]; i++);//!去重!;
    }
}

int main()
{
    //根据数学推论,两个正整数a,b要想a+b>a*b,那么a,b其中一个必为1;
    scanf("%d",&n);
    for(int i = 0;i<n;i++) scanf("%d",arr+i);
    sort(arr,arr+n);
    dfs(0);
    cout<<ans<<endl;
    return 0; 
}

手套(贪心,模拟)
在这里插入图片描述

解题思路

  						贪心思想---每次局部最优,最后全局最优(题目包含随机概率组合,问至少,那就用贪心all in)

思路:假设有一!非零!序列 a1<a2<a3<a4<…<an,最少选出多少个能够保证覆盖n种颜色?
答案是 sum(a1…an)-a1+1,类似鸽巢原理(a1显然是最小值)

所以我们利用这个公式挑某边用最少的次数覆盖所有颜色ans次,再从另一边随便拿1个return ans+1即可;

1.考虑0的影响(某种颜色手套为0,对应的left和right的这种颜色必须全拿走,否则可能配不上对不满足要求)拿走了以后sum+…的时候就不考虑这个拿走的了,min值的寻找也跟这个数无关了;
2.左边右边同时考虑 返回int ans = min(左 sum(a1…an)-a1 + 两边配不上对的手套 + 1(右随便拿一个),右 sum(a1…an)-a1 + 两边配不上对的手套 + 1(左边随便拿一个)即可; 不管左边覆盖所有颜色再从右边拿一个 还是右边覆盖所有颜色再从左边拿一个,都搞出来 挑其较小值返回即可;

class Gloves {
public:
    int findMinimum(int n, vector<int> left, vector<int> right) {
 
        int lsum = 0;
        int rsum = 0;
        int non = 0;//无效手套个数
        int lmin = 30;//这里不能初始left[0],因为这个位置偶遇可能是0,那么永远也找不到left里的最小 正 整数了
        int rmin = 30;
        for (int i = 0; i < n; i++)
        {
            if (left[i]*right[i] == 0) non += (left[i] + right[i]);//配不上对的手套 全拿走
            else
            {
                //需要拿掉的无效手套已经被全处理进none了,走到这里left[i]和right[i]都不为0,累加即可
                lsum += left[i];
                rsum += right[i];
                //min的寻找放在else里,否则non拿走两边的无效手套后,无效手套可能成为min,但是它并没有算在sum+...里,就不满足 sum(a1...an)-a1+1,定理了
                if(left[i]<lmin) lmin = left[i];
                if(right[i]<rmin) rmin = right[i];
            }
        }
        return non + min(lsum - lmin + 1, rsum - rmin + 1)  + 1;
    }
};

扑克牌大小(字符串,模拟)

在这里插入图片描述

解题思路

合理地使用substr以‘-’划分左右子字符串(两手牌);
牌依次合法出现,小王,大王要么只出现一张,如果两张同时出现则一定是王炸,且大于一切直接返回;
处理完王炸,剩下要处理的就是1-1,2-2,3-3,4-4,顺子-顺子了,string 的find妙用可以确定比较顺序;

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
string FindMax(const string& line)
{
	//存在王炸,直接处理掉,免得后续4-2还得看这个2是不是王炸的情况
	if (line.find("joker JOKER") != string::npos)
		return "joker JOKER";
	int dash = line.find('-');//stl库string中的find妙用
	//分开两手牌(string或者substr都会补‘\0’放心用)
	string car1 = line.substr(0, dash);
	string car2 = line.substr(dash + 1);
	//获取两手牌的张数
	int car1_cnt = count(car1.begin(), car1.end(), ' ') + 1;//count函数+迭代器的妙用;
	int car2_cnt = count(car2.begin(), car2.end(), ' ') + 1;
	//获取两手牌的各自第一张牌
	string car1_first = car1.substr(0, car1.find(' '));
	string car2_first = car2.substr(0, car2.find(' '));
	if (car1_cnt == car2_cnt) //两手牌的类型相同
	{
		string str = "345678910JQKA2jokerJOKER";//这里str的设定和find的使用方便巧妙地卡出了两副牌的大小
		if (str.find(car1_first) > str.find(car2_first))
			return car1;
		return car2;
	}
	//两手牌类型不同or存在炸弹(若存在炸弹返回那个炸弹(只能存在一个且不是王炸))
	if (car1_cnt == 4) //说明是炸弹
		return car1;//之前处理过王炸cnt==2的情况了,这里
	else if (car2_cnt == 4)
		return car2;
	return "ERROR";//没比出大小ERROR放最后返回;
}
int main()
{
	string line, res;
	while (getline(cin, line))
	{
		res = FindMax(line);
		cout << res << endl;
	}
	return 0;
}

杨辉三角的变形

在这里插入图片描述

解题思路

杨辉三角问题可以转换成直角三角形,通过二维数组以及对应位置关系模拟构建;
本题按照题目意思,可以发现第n行有2n - 1个元素,第i,j元素等于上一行第j - 2,j - 1,j三列元素之和(高度规律性可观察得出),每一行的第一列和最后一列都为1;
如果是第二列注意特例则只是上一列j - 1和j两个元素之和

#include<bits/stdc++.h>
using namespace std;

int main()
{
    //高n 最底下最长n*2-1
    int n;
    cin >> n;
    int m = 2*n-1;
    int arr[n][m];
    //全初始化为0
    memset(arr, 0, 4*n*m);
    for (int i = 0; i < n; i++)
    {
        //初始对角线和第一列
        arr[i][2 * i] = 1;
        arr[i][0] = 1;
    }
    
    //构建三角(注意观察三角转直角三角形后的规则!!!)
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < 2 * i; j++)
        {
            //第二列的特例!
            if (j == 1)
            {
                arr[i][j] = arr[i - 1][j] + arr[i - 1][j - 1];
                continue;
            }
            if (arr[i][j] == 0)
            {
                arr[i][j] = arr[i - 1][j - 1] + arr[i - 1][j] + arr[i - 1][j - 2];
            }
        }
    }
    
    for (int i = 0; i < m; i++)
    {
        if (arr[n-1][i] % 2 == 0)
        {
            cout << i+1<< endl;
            return 0;
        }
    }
    cout<<-1<<endl;
    return 0;
}

因为数据范围和内存限制,牛客更新了测试用例,跑不过去了,显然数组的方法不可行了,寻找数学规律;
在这里插入图片描述

通过以上的数据分析答案规律为 {-1,-1,2,3,2,4,2,3,2,4,…} ,即第n行上第一个偶数所在的下标;

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int n;
    cin>>n;
    if(n<=2)
    {
        cout<<-1<<endl;
        return 0;
    }
    int ret[] = {2,3,2,4};
    n-=3;//去掉第一行第二行的特殊情况,第三行相当对应周期下标0,第四行对应周期下表1;以此类推,直接%一次就好
    n%=4;
    cout<<ret[n]<<endl;
    return 0;
}

统计每个月兔子的总数(模拟,递推)

在这里插入图片描述

解题思路

不要死磕DP,你会发现很难控制;
一看就有强规律,不妨模拟几次看看答案规律;

#include<bits/stdc++.h>
using namespace std;

// 观察一下 答案是斐波那契 f(n) = fn-1+fn-2
int main()
{
    int n;
    cin>>n;
    int a = 1;
    int c = 1;
    int b = 1;
    n-=2;
    while(n--)
    {
        c = a+b;
        a = b;
        b = c;
    }
    cout<<c<<endl;
    return 0; 
}

字符串通配符(字符串,模拟,DFS)

在这里插入图片描述

解题思路

这道题类似于正则表达式的实现(Python网页爬虫处理url的时候接触过)
模拟,各种情况都考虑清楚;
思路2的DP算法优化到O(n^2)暂时不考虑

#include<bits/stdc++.h>
using namespace std;
class Solution {
public:
    bool Fun(const char* ms, const char* str)//递归匹配;
    {
        if (*ms == '\0' && *str == '\0') return true;
        else if (*ms == '\0' || *str == '\0') return false;
        
        else if (isalnum(*str) && isalnum(*ms))
        {
            //这里别忘记不区分大小写;
            if (tolower(*str) != tolower(*ms)) return false;
            return Fun(ms + 1, str + 1);
        }
        else if (!isalnum(*str) && !isalnum(*ms))//两个.可以匹配
        {
            return Fun(ms + 1, str + 1);
        }

        else if (*ms == '?')//只能匹1个
        {
            //str要是'.'这种,就不满足*和?只能匹字母或数字
            if (!isalnum(*str)) return false;
            return Fun(ms + 1, str + 1);
        }
        else if (*ms == '*')//匹配0 1 或多个
        {
            while (*(ms + 1) == '*') ms++;//多个*连续需要没必要,剪一下枝
            if (!isalnum(*str)) return Fun(ms + 1, str);
            return Fun(ms + 1, str) || Fun(ms + 1, str + 1) || Fun(ms, str + 1);//类似于dfs
        }
        return false;
    }

    bool isMatch(string s, string p) {
        return Fun(p.c_str(), s.c_str());
    }
};
int main()
{
    //递归向后匹
    string str;
    string ms;
    cin >> ms >> str;
    bool ret = Solution().isMatch(str,ms);
    if (ret) cout << "true" << endl;
    else cout << "false" << endl;
    return 0;
}

最长公共子序列(动态规划)

在这里插入图片描述

解题思路

要求返回最长公共子序列(可以删减中间元素)的和,分解成从s1,s2的0,0位置,子问题的形式递推到i,j字符末尾,则dp[n][m]就是答案;
1.DP[i][j]数字含义:s1字符串i位置之前和s2字符串j位置之前的最大子段和;
i,j从1开始,i||j==0的位置(第一行or第一列)dp对应的值都为0(空串没有公共子段),dp数组的初始化;
2.递推公式:

  • s1[i]==s2[j]时,dp[i][j] = dp[i-1][j-1]+1;
  • s1[i]!=s2[j]时,dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
    //经典二维dp问题
    int n = text1.size();
    int m = text2.size();
    vector<vector<int>>dp(n + 1, vector<int>(m + 1));//这里已经将dp中的内容全部初始化为0了(第一行,第一列初始为0完成)
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=m;j++)
        {
            if(text1[i-1]==text2[j-1])dp[i][j] = dp[i-1][j-1]+1;
            else
            {
                //i,j只能从dp[i-1][j]or dp[i][j-1]来了   
                dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
            }
        }
    }
    return dp[n][m];  
    }
};

最长公共子串(动态规划)

在这里插入图片描述

解题思路

与上题子序列不同,这题子串要求只能删除连续的前缀或者后缀,而不能随便删除字符进行拟合;//那么相比上一题,这个题的公共子序列只能连续,容易很多 dp转换就一个 if(s1[i-1]==s2[j-1]) dp[i][j]=dp[i-1][j-1]+1了!;

遇上题不同的是 DP[i][j]代表s1串以i位置结尾,和s2串以j位置结尾的子段长度,进行一个max记录并记录start的位置最后返回切割串即可;

这题只需要考虑s1[i]=s2[j]的dp数组更新,因为他只允许删除连续的前缀或者后缀;

//思路:动态规划经典问题,加一个start标记即可,注意将较短子串最先出现的那个输出
#include<iostream>
#include<vector>
#include<string>
using namespace std;
void findMaxCommonStr(string s1,string s2)
{
    if(s1.length()>s2.length())
            swap(s1,s2);//s1用于保存较短的子串
    int len1=s1.length(),len2=s2.length();
    int maxLen=0,start=0;
    vector<vector<int> >dp(len1+1,vector<int>(len2+1,0));
    for(int i=1;i<=len1;++i)
        for(int j=1;j<=len2;++j)
        {
            if(s1[i-1]==s2[j-1])
            {
                dp[i][j]=dp[i-1][j-1]+1;
                if(dp[i][j]>maxLen)
                {
                    maxLen=dp[i][j];
                    start=i-maxLen;//记录最长公共子串的起始位置
                }
            }
        }
   cout<<s1.substr(start,maxLen)<<endl;
}
int main()
{
   string s1,s2;
   while(cin>>s1>>s2)
   {
       findMaxCommonStr(s1,s2);
   }
   return 0;
}

最长公共字串计算(动态规划)

在这里插入图片描述

解题思路

类似于上一题,这题简化了 只要求输出max长度即可,上一题得根据这个长度找到max字串的起始位置输出子串;
DP[i][j]代表s1串以i位置结尾,和s2串以j位置结尾的子段长度;

#include<bits/stdc++.h>
using namespace std;

int main()
{
    string s1;
    string s2;
    cin >> s1 >> s2;
    int n = s1.size() ;
    int m = s2.size() ;
    int dp[n+1][m+1];
    int max = 0;
    memset(dp, 0, 4 *(n+1)*(m+1));
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (s1[i - 1] == s2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
            if (dp[i][j] > max) max = dp[i][j];
        }
    }
    cout << max << endl;
    return 0;
}

洗牌(数组,递推,模拟)

在这里插入图片描述

解题思路

一坨牌 分两摊,左右交替放,洗round轮,模拟即可;

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int a;
    cin >> a;
    while (a--)
    {
        int n, round;
        cin >> n >> round;
        int cur[2 * n];
        for (int i = 0; i < 2 * n; i++) cin >> cur[i];
        //洗牌过程
        while (round--)
        {
            vector<int>tmp{ cur,cur + 2 * n };
            for (int i = 0; i < n; i++)//找出n与left[]和right[]的关系就可以一次循环同时放入对应位置
            {

                cur[2 * i] = tmp[i];//左手的牌排放的位置
                cur[2 * i + 1] = tmp[i + n];//右手的牌排放的位置
            }

        }
        //round轮洗完了,再输出最终洗牌结果;cur
        for (int i = 0; i < 2 * n - 1; i++)
        {
            cout << cur[i] << ' ';
        }
        cout << cur[2 * n - 1] << endl;

    }
    return 0;
}

MP3光标位置(数组,滑动窗口,模拟)

在这里插入图片描述

解题思路

定长为4的滑动窗口思想,需要注意的是这里分窗口所在数组元素个数<=n(这种情况不用翻页 简化了很多)和窗口对应的数组>4(可能需要滑动)两种情况;

#include<bits/stdc++.h>
using namespace std;

int main()
{
    //滑动窗口+两端点的情况?
    int n;
    cin >> n;
    string s;
    cin >> s;
    //起始的第一页1~4
    int l = 1;
    int r = 4;
    int pos = 1;

    for (int i = 0; i < s.size(); i++)
    {
        //不用翻页的情况;
        if (n <= 4)
        {
            for (int i = 0; i < n; i++)
            {
                if (s[i] == 'U')
                {
                    if (pos == 1) pos = n;
                    else pos--;
                }
                else
                {
                    if (pos == n) pos = 1;
                    else pos++;
                }
            }
            for (int i = 1; i <= n; i++)
            {
                cout << i << ' ';
            }
            cout << endl << pos << endl;
            return 0;
        }
        
        //n>4的正常逻辑
        else
        {
            if (s[i] == 'U')
            {
                //窗口不用动
                if (pos != l) pos--;
                else//窗口需要移动了
                {
                    if (l == 1)
                    {
                        l = n - 3;
                        r = n;
                        pos = n;
                    }
                    else
                    {
                        l--;
                        r--;
                        pos--;
                    }
                }
            }
            else//D
            {
                if (pos != r)   pos++;
                else
                {
                    if (r == n)
                    {
                        l = 1;
                        r = 4;
                        pos = 1;
                    }
                    else
                    {
                        r++;
                        l++;
                        pos++;
                    }
                }
            }
        }

    }
    for (int i = l; i <= r; i++)
    {
        cout << i << ' ';
    }
    cout << endl << pos << endl;
    return 0;
}

编辑距离(字符串,动态规划)

在这里插入图片描述

解题思路

类似于上面的二维字符串dp问题;

dp[i][j]表示word1的i位置之前和word的j位置之前的字符串,重构所需要的最少操作次数;因此本题一只递推到dp[n][m]即可;

只有三种操作方法,追加,删除or替换一个字符;

因此转移方程对应DP[i][j]的取值从下列3种的最小

  1. dp[i-1][j]+1>>w1删除第i个>>(因为w1的0i-1已经和w2的0j已经重构了,w1的i位置多余了一个字符删掉),

  2. dp[i][j-1]+1>>w1的位置追加字符w2[i]>>(因为w1的0i和w2的0j-1重构了,w2的j位置还多一个字符,w1需要追加它),

  3. dp[i-1][j-1]+1>>替换;>>w1的i-1和w2的j-1位置重构了,新的dp[i][j]的位置也可以从他俩的钱一个位置+1,代表w1的i换成w2的j的字符即可;

转移方程:dp[i][j] = min(dp[i-1][j]+1,mindp[i][j-1]+1,dp[i-1][j-1]+1);

dp初始化://!!注意!! 这个数组的初始化第一行第一列需要给值0~m/n这样递增初始化

//eg:dp\[0][3],w1的0位置之前是“”,w2的3位置之前有3个字母,所以dp\[0][3]应该等于3;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fdQnStY1-1659875472055)(C:/Users/15795/Desktop/img/a8d194545ef4437b858011d7b6487d15.png)]

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n = word1.size();
        int m = word2.size();
        //dp代表w1到i位置之前与w2到j位置之前的"最短距离"
        //要么删,要么加,要么替换
        //地推方程:dp[i][j] = min 
        int dp[n+1][m+1];
        //!!注意!! 这个数组的初始化第一行第一列需要给值0~m/n这样递增初始化
        //eg:dp[0][3],w1的0位置之前是“”,w2的3位置之前有3个字母,所以dp[0][3]应该等于3;
        //初始化
        dp[0][0] = 0;
        for(int i = 1;i<=n;i++)
        {
            dp[i][0] = i;
        }
        for(int j = 1;j<=m;j++)
        {
            dp[0][j] = j;
        }

        for(int i = 1;i<=n;i++)
        {
            for(int j = 1;j<=m;j++)
            {
                //dp转移
                if(word1[i-1] == word2[j-1])
                {
                    dp[i][j] = dp[i-1][j-1];
                }
                else
                {
                    dp[i][j] = min(dp[i-1][j]+1,dp[i][j-1]+1);//减和加
                    dp[i][j] = min(dp[i][j],dp[i-1][j-1]+1);//替换
                }
            }
        }
        return dp[n][m];
    }
};

年终奖(二维数组,动态规划)

在这里插入图片描述

解题思路(dp)

题目解析:6*6棋盘格,每个格子有一个数字代表该物品价值,需要找到一条路径,该路径上的价值和最大;

  1. dp[i][j],代表走到位置ij时候的累加最大金额;

  2. 初始化:第一行第一列对应的位置是前面位置金额的累加,因为第一行只能一直向右走累加,第一列只能一直向下走累加;

  3. 转移方程:dp[i][j] = max(dp[i-1][j],dp[i][j-1])+board[i][j];//上走下来或者左走到右边,再加当前board的钱

class Bonus {
public:
    int getMost(vector<vector<int> > board) {
        // write code here
        int dp[6][6];
        memset(dp,0,6*6*4);
        dp[0][0] = board[0][0];
        //dp数组的初始化
        for(int i = 1;i<6;i++)
        {
            dp[0][i] += dp[0][i-1]+board[0][i];//第一行
            dp[i][0] += dp[i-1][0]+board[i][0];//第一列
        }
        
        for(int i = 1;i<6;i++)
        {
            for(int j = 1;j<6;j++)
            {
                //状态转移方程
                dp[i][j] = (max(dp[i-1][j],dp[i][j-1])+board[i][j]);
            }
        }
        return dp[5][5];
    }
};

解题思路(dfs)

二维数组的dfs类似于迷宫求最短路径这种问题;

class Bonus {
public:
    int sum = 0;
    int max = 0;
    void dfs(vector<vector<int> >& board,int a,int b)
    {
        sum+=board[a][b];
        if(b!=5) dfs(board,a,b+1);
        if(a!=5) dfs(board,a+1,b);
        //到终点了
        if(max<sum) max = sum;
        sum-=board[a][b];
    }
    int getMost(vector<vector<int> > board) {
        // write code here
        dfs(board,0,0);
        return max;
    }
};

迷宫问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mYvZHAoU-1659875472055)(C:/Users/15795/Desktop/img/0a9340ffc4ef45cd8e3db89773ebcf5b.png)]

解题思路

maze问题是经典的回溯算法,和dp问题一样 有着强格式模板话;

本题算是比较简单的 只有一条路径,那么dfs回溯用vector记录路径即可,注意回溯时的录入当前节点和弹出当前结点的过程,即一进入dfs就把当前位置写为走过,要是出这个dfs需要回溯,就把当前节点置为未走状态;

#include<bits/stdc++.h>
using namespace std;
int n, m;
vector<pair<int, int>>tmp;
vector<pair<int, int>>best;
void dfs(vector<vector<int>>& maze, int a, int b)
{
	if(maze[a][b]==1) return;//位置不合法;
	
    maze[a][b] = 1;//位置合法,设置为已经走过 继续dfs向下回溯找路径;  
    tmp.push_back({ a,b });//记录达到的节点;
    
    if (a == n - 1 && b == m - 1)//走到终点
    {
        if (best.empty() || tmp.size() < best.size()) best = tmp;//这里也能把最短的记录到;
        tmp.pop_back();
        maze[a][b] = 0;
        return;
    }
    //上下左右寻找出路;
    if (a != 0)
        dfs(maze, a - 1, b);
    if (a != n - 1)
        dfs(maze, a + 1, b);
    if (b != 0)
        dfs(maze, a, b - 1);
    if (b != m - 1)
        dfs(maze, a, b + 1);
    //恢复路径 回溯;
    maze[a][b] = 0;
    tmp.pop_back();
    return;
}
int main()
{
    cin >> n >> m;
    vector<vector<int>>maze(n, vector<int>(m, 0));
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            cin >> maze[i][j];
        }
    }
    dfs(maze, 0, 0);//从0,0开始寻找路径
    for (auto& e : best)
    {
        printf("(%d,%d)\n", e.first, e.second);
    }
    return 0;
}

星际密码(递推规律)

在这里插入图片描述

解题思路

首先,给定整数x,则翻译的密码就是给定矩阵[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AUYFlRzJ-1656125056994)(C:/Users/15795/Desktop/img/image-20220625103532957.png)]的x次幂;

乘几次观察得出,这个密码事以f(1) = 1,f(2) = 2的fib数列构成,那么问题就简单很多了;

下面是比较挫的代码;

// write your code here cpp
#include<bits/stdc++.h>
using namespace std;
int arr[10001];
void f2(int& a)//只存后四位,fib(x)可能int存不下 没关系 存后四位即可;
    //这个函数完全可以用 %10000来存后四位代替.....!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
{
    if (a <= 9999) return;
    string tmp = to_string(a);
    a = stoi(tmp.substr(tmp.size() - 4));
}
void Init()//初始化fib数组 因为会连续输入好几个x 让你求fib(x),用WHILE那种循环求fib(x)的话的话会重复大量计算,超时!!应该用全局数组一遍记录好达到剪枝;
{
    arr[1] = 1;
    arr[2] = 2;
    for (int i = 3; i <= 10000; i++)
    {
        arr[i] = arr[i - 1] + arr[i - 2];
        f2(arr[i]);//强制存后四位
    }
}

void f(int x)//这里完全可以用printf("%04d",x);来代替....!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
{
    //因为肯可能求10000个fib  越界了,题目有要求只需要后四位,就对后四位操作;
    string ret = "0000";
    ret += to_string(arr[x]);
    int len = ret.size();
    cout << ret.substr(len - 4);
}
int main()
{
    int flag;
    Init();
    while (cin >> flag)
    {
        for (int i = 0; i < flag; i++)
        {
            int x;
            cin >> x;
            f(x);
        }
        cout <<endl;
    }
    return 0;
}

优美版本(善于观察,学以致用。。。)

在这里插入图片描述

数根(字符串,高精度,整型,递归)

在这里插入图片描述

解题思路

即便是想到了应该cin到一个string里,因为数据范围最长可以到100多位数字!

但是,一直在同一个string里操作各位相加还有进位递归啥的不方便;

善于观察可以看出 即便是100多个9,累加也不会超int范围,那我们就用string浅录入过度一下,手动累加一边放入一个int中再用这个int进行递归逐位相加就好啦!

#include<bits/stdc++.h>
using namespace std;

int f(int x)//计算一个整数的各位和,最终为个位数时递归终止返回
{
    if(x<10)
    {
        return x;
    }
    
    int tmp = 0;
    while(x)
    {
        tmp += x%10;
        x/=10;
    }
    return f(tmp);
}

int main()
{
    string s;//最多输入长度为102的数字组合,就算102个9相加int也能装下;//
    while(cin>>s)
    {
        int sum = 0;
        for(int i = 0;i<s.size();i++)//则相当于放入第一次输入的大整数通过string将各位累加放入了int sum
        {
            sum+=s[i] - '0';
        }
        cout<<f(sum)<<endl;
    }
    return 0; 
}

跳台阶扩展问题(变态版青蛙跳台阶,递归,数学)

在这里插入图片描述

解题思路

//青蛙跳台阶;
//1-1 2-2 3-4…
//和青蛙一次只能1or2步思路类似;
//达到第n的方法可以:
//从第n-1直接跨1个台阶,从第n-2直接跨2个台阶,从n-3一步跨3个台阶。。。从n-(n-1)个台阶一次跨n-1到终点
//递推公式:f(n) = f(0)+f(1)+… f(n-1) 普通青蛙跳台阶的递推公式:f(n) = f(n-1)+f(n-2)//一次只能跳1 or 2个台阶;’

class Solution {
public:
    int jumpFloorII(int number) {
        if(number == 1) return 1;//f(1) = 1
        int sum = 1;//f(0) = 1;
        for(int i = 1;i<number;i++)
        {    
            sum+=sum;
        }
        return sum;
    }
};

高精度加法

string Add(string& a, string& b)//高精度加
{
    string ret;
    int ra = a.size() - 1;
    int rb = b.size() - 1;
    int carry = 0;//进位
    while (ra >= 0 || rb >= 0 || carry > 0)
    {
        int tmp = 0;
        
        tmp += ra < 0 ? 0 : a[ra] - '0';
        tmp += rb < 0 ? 0 : b[rb] - '0';
        tmp += carry;

        carry = tmp / 10;
        tmp %= 10;

        ret += '0' + tmp;
        ra--;
        rb--;
    }
    reverse(ret.begin(), ret.end());
    return ret;
}

高精度减法

bool Cmp(string &a,string &b)//返回a<b
{
    if (a.size() < b.size())return true;
    else if (a.size() > b.size()) return false;
    else {
        for (int i = 0; i < a.size(); i++) {
            if (s[i] < b[i])return true;
            else if(s[i] > b[i]) return false;
        }
        return false;//a == b 
    }
}

string Sub(string &a,string &b)// a - b
{
    int flag = 0;
    //保证 大 - 小
    
      //if (atoi(a.c_str()) < atoi(b.c_str())) {//这样比其实也是有风险的,万一高精度直接越界
    //    flag = 1;//最后需要输出'-'符号;
    //    swap(a, b);
    //}
    if(Cmp(a,b)){
        flag = 1;//最后需要输出'-'符号;
        swap(a, b);
    }
    
    string ret;
    int ra = a.size() - 1;
    int rb = b.size() - 1;
    while (ra>=0||rb>=0)
    {

        if (ra>=0&&rb<0) {//被减数把减数减完了,还有剩余 直接挪下来;
            ret += a[ra];
            ra--;
            continue;
        }

        if (a[ra] < b[rb]) {
            //需要借位;
            int index = ra-1;
            while (a[index] == '0') index--;//找到借的那一位 位置下表

            a[index]--;//借的那一位被拿掉;

            for (int i = ra - index ; i > 1; i--) {
                a[++index] = '9';//路途中补9
            }
           ret +=a[ra]-b[rb]+10 +'0';//进了10 然后再减b

        }
        else ret += a[ra]-b[rb] +'0';

        ra--;
        rb--;
    }
    
    
    

    if (flag) ret += '-';
    reverse(ret.begin(),ret.end());
    //去除借位之后可能产生的首部无效0(最高位为1 并被借走就这样多了个无效0);
    if (ret[0] == '0')ret.erase(0,1);
    return ret;
}

理论上,有了高精度加法和减法,就能(通过交换律)完成无论是正数还是负数之间的加减运算了!

高精度乘法

string f(string &a,string &b)
{
    string ret = "0";
    int ra = a.size()-1;
    int rb = b.size()-1;
    int carry = 0;//进位

    int flag = 0;
    for (int i = rb; i >= 0; i--) {//一共走乘数长度轮

        string stmp;//记录每一轮的结果!
        carry = 0;//进位清零!!
        for (int i = 0; i < flag; i++) stmp += '0';//乘数下一位的轮次,需要在上一倍基础上扩大十倍后再加下一轮的结果!

        for (int j = ra; j >= 0; j--) {//乘数的某一位,分别与被乘数的每一位相乘,并记录结果进入ret;
            int tmp = 0;
            
            tmp += (a[j] - '0') * (b[i] - '0')+carry;
            carry = tmp / 10;
            tmp %= 10;

            stmp += tmp + '0';
        }

        if (carry)stmp += carry+'0';//看还有没有进位
        
        reverse(stmp.begin(), stmp.end());
        ret = Add(ret,stmp);//高精度加法
        flag++;
    }
    return ret;
}

不用加减乘除做加法

在这里插入图片描述

考察位运算:

a^b和(a&b)<<1结合使用;

a^b相当于把不进位的0和1加起来了,进位的1和1抵消为0;

(a&b)<<1相当于把需要进位的两个1拿出去进位了,下一轮再加起来 (原进位位置已经被^把两个1消掉了)

class Solution {
public:
    int Add(int num1, int num2) {
    int a = num1;
    int b = num2;
    int carry;
    int sum = a ^ b;

    while (carry = (a&b)<<1) {

        sum = a ^ b;
        
        a = sum;//注意在sum与carry^之前,需要先记录当前sum,之后才能看当前sum与carry有没有进位,需不需要继续while循环+
        b = carry;
        
        sum = sum^carry;
        
       
    }
    return sum;
}
};

判断三角形

在这里插入图片描述

记住定理,三角形成立条件:任意两边之和大于第三边;

数据范围过大,使用高精度加法,进行判定;

#include<iostream>
#include<algorithm>
#include<stdlib.h>
using namespace std;

bool Cmp(string& s, string& b)//判断大整数a是不是大于b
{
    if (s.size() > b.size())return true;
    else if (s.size() < b.size()) return false;
    else {
        for (int i = 0; i < s.size(); i++) {
            if (s[i] > b[i])return true;
            else if(s[i]<b[i])return false;
        }
        return false;
    }
    return false;
}

string Add(string a, string b)//高精度加法
{
    string ret;
    int ra = a.size() - 1;
    int rb = b.size() - 1;
    int carry = 0;//进位
    while (ra >= 0 || rb >= 0 || carry > 0)
    {
        int tmp = 0;

        tmp += ra < 0 ? 0 : a[ra] - '0';
        tmp += rb < 0 ? 0 : b[rb] - '0';
        tmp += carry;

        carry = tmp / 10;
        tmp %= 10;

        ret += '0' + tmp;
        ra--;
        rb--;
    }
    reverse(ret.begin(), ret.end());
    return ret;
}

int main()
{
    string a, b, c;
    while (cin >> a >> b >> c) {
        string tmp1 = Add(a, b);
        string tmp2 = Add(a, c);
        string tmp3 = Add(c, b);
        if (Cmp(tmp1, c) && Cmp(tmp2, b) && Cmp(tmp3, a))//Add(q,b)>c 这种判断是错的,因为两个string类型表示的长int 不能直接用string原生的>进行大小判断;
            cout << "Yes" << endl;
        else cout << "No" << endl;
    }


    return 0;

}
  • 39
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魏天乐大帅哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值