【反思与总结---1】在线OJ①

本文深入探讨了一系列算法问题,包括最大水平值计算、字符串操作、排序子序列分析、回文串生成方法、连续子数组和的最大值计算等。通过具体代码示例和反思,分享了解题思路和性能优化技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最大水平值、字符串remove

题目要求:

<1>.第一行输入行数n,第二行输入3*n个整数输出
<2>.水平值为队伍第二高水平值
<3>.输出水平值总和最大值

解题思路:

<1>.第一反应,先排序
<2>.按照题目要求,三个数中只能取一个数,i=0;i<n;++i
<3>.取的数的下标是3n-2i;行数多少就取几个
<4>.返回这些数相加的和

代码示例:

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

int main()
{
    int n;
    cin>>n;

    vector<int> array(3*n);

    for(int i=0;i<3*n;++i)
    {
        cin>>array[i];
    }

    sort(array.begin(), array.end());

    int total=0;

    for(int i=1;i<=n;++i)
    {
        total+=array[3*n-2*i];
    }

    cout<<total<<endl;

    return 0;
}

防止数据过大过多,将int修改为long,测试用例也只通过70%,后续进行修改…

题目要求:

<1>.输入字符串they are students.
<2>.输入字符串aeiou
<3>.输出结果thy r stdnts.

解题思路:

<1>.先利用string类输入两个字符串
<2>.字符串不以空格作为结束标志需要用getline(cin,s)
<3>.第一层循环对aeiou遍历,第二层对they are students遍历
<4>.如果存在aeiou,就是要erase删掉
<5>.pos就是遍历的时候的i,erase(i,1)表示只删除那一位的元素

代码示例:

#include<iostream>
#include<string>

using namespace std;

int main()
{
    string s1;
    string s2;

    getline(cin,s1);
    getline(cin,s2);

    for(int i=0;i<s2.length();++i)
    {
        for(int j=0;j<s1.length();++j)
        {
            if(s1[j]==s2[i])
            {
                s1.erase(j,1);
            }
        }
    }

    cout<<s1<<endl;

    return 0;
}

反思:

<1>.做题时没有使用vector,静态数组空间一次给太大满足题意但空间浪费太大
<2>.自己写冒泡排序性能太差,规定时间内算法没有运行完,其实应该写快排,但是不一定写的对
<3>.sort函数的头文件是algorithm,vector并没有sort方法
<4>.数据太大时定义变量使用long类型
<5>.多个用例同时测试需要循环输入,使用while(cin>>n)

正确代码示例:

#include<iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main()
{
	int n = 0;
	long long a = 0;

	while (cin >> n)
	{

		vector<int> array(3 * n);

		for (int i = 0; i < 3*n; ++i)
		{
			cin >> array[i];
		}

		sort(array.begin(), array.end());

		for (int i = 1; i <= n; ++i)
		{
			a += array[3 * n - 2 * i];
		}

		cout << a << endl;
	}

	system("pause");
	return 0;
}

<1>.果然如我所想,使用暴力查找性能差的自己都满意不了
<2>.不要使用erase,效率太差,使用新的字符串,把没出现过的字符+=过去
<3>.计数排序使用过的思想hashtable[s1[i]]++;

写出正确的代码,不算什么,写出性能好bug少的代码,才算厉害…

加油吧…

正确代码示例:

#include <iostream>
#include <string>

using namespace std;

int main()
{
	string s1;
	string s2;

	getline(cin, s1);
	getline(cin, s2);

	int hashtable[256] = { 0 };

	for (int i = 0; i < s2.length(); ++i)
	{
		hashtable[s2[i]]++;
	}

	string ret;

	for (int i = 0; i < s1.length(); ++i)
	{
		if (hashtable[s1[i]] == 0)
		{
			ret += s1[i];
		}
	}

	cout << ret << endl;

	return 0;
}

排序子序列、字符串单词逆置

题目要求:

<1>.输入描述:输入的第一行为一个正整数n(1 ≤ n ≤ 10^5)
<2>.第二行包括n个整数A_i(1 ≤ A_i ≤ 10^9),表示数组A的每个数字
<3>.输出描述: 输出一个整数表示牛牛可以将A最少划分为多少段排序子序列

解题思路:

<1>.找出波峰和波谷的个数
<2>.返回的序列个数=波峰和波谷的个数+1;

代码示例:
通过用例只有20%,等于没做出来,所以直接等后续修改…

题目要求:

<1>.输入一个字符串i like lv.
<2>.将这个字符串的单词逆置
<3>.输出lv. like i

解题思路:

<1>.首先将字符串完全逆置,用algorithm的reverse函数
<2>.字符串变为.vl ekil i
<3>.将每个单词逆置,单词之间是用空格隔开的
<4>.reverse参数为s.begin(),s.end(),此时无法使用
<5>.创建临时字符型变量,首尾交换完成逆置
<6>.输出lv. like i

代码示例:

#include<iostream>
#include<algorithm>
#include<string>

using namespace std;

int main()
{
    string s;
    getline(cin,s);

    reverse(s.begin(),s.end());

    int i=0;

    while(s[i+1]!='\0')
    {
        int begin=0;
        int end=0;

        if(s[i]!=' ')
        {
            begin=i;
        }

        while(s[i]!='\0'&& s[i]!=' ')
        {
            i++;
        }
        end=i-1;

        while(end>begin)
        {
            char tmp=s[begin];
            s[begin]=s[end];
            s[end]=tmp;
            end--;
            begin++;
        }

        if(i!=s.length())
        {
            i++;
        }
    }

    cout<<s<<endl;

    return 0;
}

反思:

<1>.题目没有读懂,没清楚的理解非递增子序列和非递减子序列
<2>.a[i]<a[i+1]时递增,a[i]>=a[i+1]就是非递增
<3>.数组长度给n+1防止越界

正确代码示例:

#include<iostream>
#include <vector>

using namespace std;

int main()
{
	int n;
	cin >> n;

	vector<int> v(n + 1);

	for (int i = 0; i < n; ++i)
	{
		cin >> v[i];
	}

	int i = 0;
	int count = 0;

	while (i < n)
	{
		if (v[i] < v[i + 1])
		{
			while (i < n && v[i] <= v[i + 1])
			{
				i++;
			}
			count++;
			i++;;
		}
		else if (v[i] == v[i + 1])
		{
			i++;
		}
		else
		{
			while (i < n && v[i] >= v[i + 1])
			{
				i++;
			}
			count++;
			i++;
		}
	}

	cout << count << endl;

	return 0;
}

<1>.对reverse函数的理解不透彻,用法了解不彻底
<2>.对参数的理解是错误的,认为只有s.begin()这种可以用
<3>.自己定义成int start=s.begin();是错误
<4>.正确用法为auto start=s.begin();
<5>.迭代器可以暂时当作指针来用

能尽量不自己写方法就不自己写,自己写容易出错…而且效率差…

正确代码示例:

#include<iostream>
#include<algorithm>
#include<string>

using namespace std;

int main()
{
	string s;
	getline(cin, s);

	reverse(s.begin(), s.end());

	auto start = s.begin();

	while (start != s.end())
	{
		auto end = start;

		while (*end != ' ' && end != s.end())
		{
			end++;
		}

		//走到这里说明遇到空格,一个单词已经遍历完,开始逆置
		reverse(start, end);

		if (end != s.end())
		{
			start = end + 1;
		}
		else
		{
			start = end;
		}
	}

	cout << s << endl;

	return 0;
}
	在32位cpu上选择缺省对齐的情况下,有如下结构体定义:
	struct A{
	    unsigned a : 19;
	    unsigned b : 11;
	    unsigned c : 4;
	    unsigned d : 29;
	    char index;
	};
	则sizeof(struct A)的值为()

a和b放在一起,30比特位,c只能单独放,d是29位的所以和c放不在一起,只能单独放,最后的index是8位,也只能单独放,所以字节位4+4+4+4=16

找出超过数组长度一半的数字、输出字符串连续最长子串

题目要求:

<1>.如果数组中一个数字出现次数超过数组长度一半,输出这个数
<2>.如果没超过一办,就输出0

解题思路:

<1>.对已排序数组来说,出现次数最多的就是中位数
<2>.只需要判断中位数出现的次数有没有超过数组长度的一半
<3>.统计中位数的个数,numbers[i]==numbers[numbers.size()>>1];
<4>.如果count>numbers.size(),就输出中位数,反之输出0;

代码示例:

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int>numbers)
    {
        int count=0;
        sort(numbers.begin(),numbers.end());

        for(int i=0;i<numbers.size();++i)
        {
            if(numbers[i]==numbers[numbers.size()>>1])
            {
                count++;
            }
        }

        if(cout>(numbers.size()>>1))
        {
            return numbers[numbers.size()>>1];
        }
        return 0
    }

题目要求:

<1>.输入测试用例字符串,如abcd2333a123456789
<2>.找出连续出现最长的字符串,123456789,将其输出

解题思路:

暂无

代码示例:
暂无

等待后续修改…

反思:

<1>.用到排序的话,万一面试官非要让你手写快排怎么办呢?
<2>.第一次的for循环,是为了找出那个出现次数比其他数字多的数字
<3>.先把第一个存起来,i从1开始,和保存的对比,相等time就加一,不想等就减一
<4>.如果time=0的时候保存当前数字
<5>.第二次的循环是为了统计这个数字出现的次数
<6>.最后和数组长度一半对比,超过就输出result,没超过就输出0

正确代码示例:

class Solution
{
public:
	int MoreThanHalfNum_Solution(vector<int> numbers)
	{
		if (numbers.empty())
		{
			return 0;
		}

		int time = 1;

		int result = numbers[0];

		for (int i = 1; i < numbers.size(); ++i)
		{
			if (time==0)
			{
				result = numbers[i];
			}
			else if (numbers[i] == result)
			{
				time++;
			}
			else
			{
				time--;
			}
		}

		time = 0;

		for (int i = 0; i < numbers.size(); ++i)
		{
			if (numbers[i]==result)
			{
				time++;
			}
		}

		if (time>(numbers.size() >> 1))
		{
			return result;
		}
		else
		{
			return 0;
		}

	}
};

我说我把题目看错了你敢信?

<1>.求的是最长连续数字串
<2>.遍历输入的字符串,找出数字部分,存起来,下次在找到数字部分
<3>.对比它们size的大小, 如果比之前的长,就保存新的
<4>.如果没有,就把当前保存的清除

正确代码示例:

#include<iostream>
#include <string>

using namespace std;

int main()
{
	string s1;
	string s2;
	string s3;

	cin >> s1;

	for (int i = 0; i <= s1.length(); ++i)
	{
		if (s1[i] >= '0'&&s1[i] <= '9')
		{
			s2 += s1[i];
		}
		else
		{
			if (s3.size() >= s2.size())
			{
                s2.clear();
			}
			else
			{
				s3=s2;
			}
		}
	}

	cout << s3 << endl;

	return 0;
}

const放在 * 的左边,表示值不可以修改,const放在 * 的右边,表示指向不可以修改…

左定值,右定向…

计算糖果、进制转换

题目要求:

<1>.输入四个数字作为A - B, B - C, A + B, B + C的值
<2>.计算出A,B,C的值,并且保证只有一组满足

解题思路:

<1>.定义四个变量a,b,c,d保存值
<2>.消元之后可得A=(a-c)>>1;B=(c-a)>>1或(b+d)>>1;C=(d-b)>>1;
<3>.如果两个B的值计算出来相等,那就是存在解,否则输出No

代码示例:

#include<iostream>
#include<vector>

using namespace std;

int main()
{
    int a,b,c,d;

    int A=0;
    int B=0;
    int C=0;

    while(cin>>a>>b>>c>>d)
    {
        A=(a-c)>>1;
        B=(c-a)>>1;
        C=(d-b)>>1;

        if(((c-a)>>1)==((b+d)>>1))
        {
            cout<<A<<' '<<B<<' '<<C<<endl;
        }
        else
        {
            cout<<"No"<<endl;
        }
    }

    return 0;
}

题目要求:

<1>.输入一行,空格隔开,十进制数和要转成的进制数
<2>.N>9要考虑16进制转换结果,A=10…F=15;
<3>.输出转换之后的结果

解题思路:

<1>.数位遍历,短除法
<2>.把结果保存到数组里,但是最后要逆序打印
<3>.vector:: reverse_iterator rit

代码示例:

#include<iostream>
#include<vector>

using namespace std;

int main()
{
    int M,N;

    vector<int> v;

    while(cin>>M>>N)
    {
        while(M!=0)
        {
            v.push_back(M%N);
            M%=N;
        }

        if(N<=9)
        {
            vector<int>:: reverse_iterator rit=v.rbegin();

            while(rit!=v.rend())
            {
                cout<<*rit;
                rit++;
            }
        }
        else
        {
            vector<int>:: reverse_iterator rit=v.rbegin();

            while(rit!=v.rend())
            {
                switch(*rit)
                {
                    case 10:cout<<'A';
                        break;
                    case 11:cout<<'B';
                        break;
                    case 12:cout<<'C';
                        break;
                    case 13:cout<<'D';
                        break;
                    case 14:cout<<'E';
                        break;
                    case 15:cout<<'F';
                        break;
                    default:cout<<*rit;
                        break;
                }
                rit++;
            }
        }
    }

    return 0;
}

通过测试用例只有70%…等待后续修改…

反思:

int main()
{
	int a[5] = { 1, 3, 5, 7, 9 };
	int *p = (int *)(&a + 1);
	printf("%d,%d", *(a + 1), *(p - 1));
	
	return 0;
}

数组名单独出现,隐式转换为指针,但是&数组名,指向整个数组,&a+1,指向9之后,p-1刚好指到9

#include<stdio.h>
int cnt = 0;
int fib(int n)
{
	cnt++;
	if (n == 0)
		return 1;
	else if (n == 1)
		return 2;
	else
		return fib(n - 1) + fib(n - 2);
}

void main()
{
	fib(8);
	printf("%d", cnt);
}

cnt表示的是调用次数,当n=0,n=1时,cnt调用都是一次

当n=2时,调用次数为f(1)+f(0)+自身cnt++,等于3

类推,n=8,调用次数为f(7)+f(6)+1,等于67

<1>.什么逆序打印真的low爆了好吗?为什么不直接一个reverse,然后直接cout
<2>.用switch也是没谁了,标准答案是给一个table=“0123456789ABCDEF”
<3>.按进制转换,添加到s中就可以了,如果是负数,转成正数,flag置为true
<4>.之前没考虑到负数的问题,负数的话最后+=负号

正确代码示例:

#include<iostream>
#include<algorithm>
#include<string>

using namespace std;

int main()
{
    string s;
    string table("0123456789ABCDEF");

    bool flag=false;

    int m,n;

    cin>>m;
    cin>>n;

    if(m<0)
    {
        flag=true;
        m=0-m;
    }

    while(m!=0)
    {
        s+=table[m%n];
        m/=n;
    }

    if(flag==true)
    {
        s+='-';
    }

    reverse(s.begin(),s.end());

    cout<<s<<endl;

    return 0;
}

连续子数组的和的最大值、插入字符串返回可以成为回文串的方法数

题目要求:

<1>.一个数组有 N 个元素,求连续子数组的最大和
<2>.输入描述:输入为两行。 第一行一个整数n(1 <= n <= 100000),表示一共有n个元素 第二行为n个数,即每个元素,每个整数都在32位int范围内
<3>.输出描述:所有连续子数组中和最大的值

解题思路:

<1>.创建一个变量sum,把每个元素累加起来
<2>.把数组第一个元素作为max保存起来,如果累加的结果比max大,更新max
<3>.sum如果小于0,说明存在负数,把sum清零,重新朝后累加

代码示例:

#include<iostream>
#include<vector>

using namespace std;

int main()
{
    long long n;
    cin>>n;

    vector<int> v(n);

    for(int i=0;i<v.size();++i)
    {
        cin>>v[i];
    }

    int sum=0;
    int max=v[0];

    for(int i=0;i<v.size();++i)
    {
        sum+=v[i];

        if(sum>max)
        {
            max=sum;
        }

        if(sum<0)
        {
            sum=0;
        }
    }

    cout<<max<<endl;

    return 0;
}

题目要求:

<1>.输入两个字符串s1.s2
<2>.将s2插入到s1中,可以使s1成为回文串,方法加1
<3>.返回方法数

解题思路:

<1>.使用string类的insert方法,将s2,分别插入s1的各个位置
<2>.判断s1是否为回文串
<3>.将s1逆置,如果逆置之后和原串一样,就是回文串
<4>.插入之后s1会发生改变,所以用string s3(s1);
<5>.每次插入之后判断s3是不是回文串,判断完之后s3=s1;
<6>.是回文串ret++,最终输出ret

代码示例:

#include<iostream>
#include<algorithm>
#include<string>

using namespace std;

bool Check(string s1)
{
    string s2(s1);

    reverse(s1.begin(),s1.end());

    if(s1==s2)
    {
        return true;
    }
    return false;
}

int main()
{
    string s1;
    string s2;

    getline(cin,s1);
    getline(cin,s2);

    int ret=0;

    string s3(s1);

    for(int i=0;i<s1.length();++i)
    {
        s3.inserrt(s2,i);

        if(Check(s3))
        {
            ret++;
        }

        s3=s1;
    }

    cout<<ret<<endl;

    return 0;
}

只通过80%用例…等待后续修改…

反思:

<1>.理解能力好像不是很行…我感觉理解不了题目的意思…
<2>.sum1表示以v[i-1]元素为最后一个元素的连续子数组的和
<3>.如果sum1<0,就算加上下一个也不会计算出更大的值
<4>.sum2如果小于0,置为0重新从这个元素开始计算
<5>.如果sum2大于保存连续子数组最大和的max,就把max更新为sum2

正确代码示例:

#include<iostream>
#include<vector>

using namespace std;

int main()
{
    long long n;
    cin>>n;
    
    vector<int> v(n);
    
    for(int i=0;i<v.size();++i)
    {
        cin>>v[i];
    }
    
    int sum1=0;
    int sum2=0;
    int max=v[0];
    
    for(int i=0;i<v.size();++i)
    {
        if(sum1>=0)
        {
            sum2=sum1+v[i];
        }
        else
        {
            sum2=v[i];
        }
        
        if(sum2>max)
        {
            max=sum2;
        }
        
        if(sum2<0)
        {
            sum2=0;
        }
        
        sum1=sum2;
    }
    
    cout<<max<<endl;
    
    return 0;
}

<1>.思路没什么问题,但是一些细节不行
<2>.insert的使用方法不熟悉
<3>.循环少了一个=号导致部分用例最后结尾的位置没有插入

正确代码示例:

#include<iostream>
#include<algorithm>
#include<string>

using namespace std;

bool Check(string s1)
{
    string s2(s1);

    reverse(s1.begin(),s1.end());

    if(s1==s2)
    {
        return true;
    }
    return false;
}

int main()
{
    string s1;
    string s2;

    getline(cin,s1);
    getline(cin,s2);

    int ret=0;

    string s3(s1);

    for(int i=0;i<=s1.length();++i)
    {
        s3.insert(i,s2);

        if(Check(s3))
        {
            ret++;
        }

        s3=s1;
    }

    cout<<ret<<endl;

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值