参数解析,跳石板

这篇博客探讨了C++中的参数解析,详细解释了如何处理包含空格的参数,并提供了两种不同的解析方法。此外,还介绍了跳石板问题,这是一个涉及到动态规划的数学挑战,博主给出了求解最少跳跃次数的思路和伪代码。

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

问答题

问答题1:下面C程序的输出结果:

int i=0,a=1,b=2,c=3;
i=++a||++b||++c;
printf("%d %d %d %d",i,a,b,c);

首先 i 的右边会发生短路,如果a 是非0,则后面不会计算,而且右边整个是一个表达式,对于 i 只有两个值,要么是0,要么是 1,由于左边是真,所以 i 为1

答案是:1 2 2 3

问答题2:下面哪个操作符不能被重载?
在这里插入图片描述
提示:c++不能重载的运算符有.成员访问运算符::域解析符?:条件语句运算符*成员指针访问运算符sizeoftypeidstatic_castdynamic_castinterpret_cast

. 和 * 两个运算符不能重载是为了保证访问成员的功能不能被改变,域运算符和 sizeof 运算符的运算对象是类型而不是变量或一般表达式,不具备重载的特征,三目运算符可以看做 if else,由于重载的本质是函数的调用,if 和 else 语句应该是分支执行的,如果可以被重载,则无法实现分支这一功能.

在这里插入图片描述
问答题3:分析这段程序的输出?

class B{
public:
	B(){
		cout << "default constructor" << " ";
	}
	~B(){
		cout << "destructed" << " ";
	}
	B(int i) : data(i){
		cout << "constructed by parameter" << data << " ";
	}
private: int data;
};

B Play(B b){
	return b;
}

int main(int argc, char *argv[]) {

	Play(5);

	system("pause");
	return 0;
}

提示:首先调用函数 Play,在进行传参的时候,会将 5 赋值给形参b,这时候调用有参数的构造函数,输出 constructed by parameter5,在 Play 函数返回之前,由于形参需要释放,所以第一次调用 析构函数释放形参 b,输出一个 destructed,之后会进行拷贝构造函数,将返回的对象拷贝给 temp,但是由于类中没有显示定义,所以调用的是编译器默认的拷贝构造函数,这一步骤并没有输出任何内容,最后释放 temp 对象,再调用一次析构函数输出一次 destructed;

答案:constructed by parameter5 destructed destructed

问答题4: 下列程序输出什么?

class A{
public:
	virtual void print(){
		cout << "A::print()" << "\n";
	}
};

class B : public A {
public: 
	virtual void print() {
		cout << "B::print()" << "\n";
	}
};

class C : public A {
public:
	virtual void print() {
		cout << "C::print()" << "\n";
	}
};

void print(A a) {
	a.print();
}

int main(){
	A a, *aa, *ab, *ac;
	B b;
	C c;
	aa = &a;
	ab = &b;
	ac = &c;
	a.print(); // A::print()
	b.print(); // B::print()
	c.print(); // C::print()
	aa->print();//A::print()
	ab->print();//B::print()
	ac->print();//C::print()
	print(a);//A::print()
	print(b);//A::print()
	print(c);//A::print()

	system("pause");
	return 0;
}
	a.print(); // A::print()
	b.print(); // B::print()
	c.print(); // C::print()
	这段程序分别是对应的类对象调用各自的函数,
	a,b,c分别属于 类A,B,C,调用各自类的函数,
	aa->print();//A::print()
	ab->print();//B::print()
	ac->print();//C::print()
	这段程序是父类指针指向了子类对象,本来aa,ab,ac都是属于
	类A的对象,但是分别指向了子类A,B,C,所以调用各自类的函数
    print(a);//A::print()
	print(b);//A::print()
	print(c);//A::print()
	最后一段程序,a,b,c本来是类A,B,C的对象,但是在进行函数
	传参的时候,由于是传值类型,所以自动转换为类A的类型,
	所以都调用类A的函数.
	
	另外如果把函数传值类型改为传引用的类型,那么结果就是分别
	调用A,B,C类的函数

问答题5:下列选项不能正确输出 hello 的是?

#include<stdio.h>
struct str_t{
   long long len;
   char data[32];
};
struct data1_t{
   long long len;
   int data[2];
};
struct data2_t{
   long long len;
   char *data[1];
};
struct data3_t{
   long long len;
   void *data[];
};
int main(void){
   struct str_t str;
   memset((void*)&str,0,sizeof(struct str_t));
   str.len=sizeof(struct str_t)-sizeof(int);
   //str.len = 36;
   
   snprintf(str.data,str.len,"hello");//VS下为_snprintf
   ____________________________________;
   ____________________________________;
   return 0;
}

A选项:

  1. struct data3_t* pData=(struct data3_t* )&str;
  2. printf(“data:%s%s\n”,str.data,(char*)(&(pData->data[0])));

B选项:

  1. struct data2_t * pData=(struct data2_t * )&str;
  2. printf(“data:%s%s\n”,str.data,(char * )(pData->data[0]));

C选项:

  1. struct data1_t * pData=(struct data1_t * )&str;
  2. printf(“data:%s%s\n”,str.data,(char*)(pData->data));

D选项:

  1. struct str_t * pData=(struct str_t * )&str;
  2. printf(“data:%s%s\n”,str.data,(char*)(pData->data));

首先我们要搞明白要输出hello ,就必须得到 'h '地址;
在这里插入图片描述

编程题

1、 参数解析

在命令行输入如下命令:xcopy /s c:\ d:\
各个参数如下:
参数1:命令字xcopy
参数2:字符串/s
参数3:字符串c:\
参数4:字符串d:\
请编写一个参数解析程序,实现将命令行各个参数解析出来

解析规则如下:
参数分隔符为空格

对于用" "包含起来的参数,如果中间有空格,不能解析为多个参数。比如在命令行输入
xcopy /s "C:\program files" "d:\"时,

参数仍然是4个,第3个参数应该是字符串C:\program files,注意输出参数时,需要将" "去掉,引号不存在嵌套情况

输入描述:输入一行字符串,可以有空格
输出描述:输出参数个数,分解后的参数,每个参数都独占一行

示例:
输入:xcopy /s c:\ d:\
输出:
4
xcopy
/s
c:\
d:\

方法一 :遍历一遍字符串,如果遇到空格,则判断空格后面是不是引号,如果不是引号,则把该空格位置换成'\n' ,如果该空格位置的下一位是引号,则把引号位置的字符改为'\n',然后再寻找下一个引号,把结尾的引号改为'\n'

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

int main(){
	string str;
	int count = 0;
	while (getline(cin, str)) {
		//先整体遍历一遍
		for (size_t i = 0; i < str.size(); ++i){
			//如果遇到空格,则先跳过,判断它下一位是不是引号?
			if (str[i] == ' '){
				++i;
				if (str[i] == '\"' || str[i] == '\'') {
					//如果是引号则把引号的位置置换为\n,前面那个空格可以不管了
					str[i--] = '\n';
					++count;
					//遍历一直到下一个引号为止
					while (str[i++] != '\"'){}
					str[i] = '\0';
				//如果没有引号就将空格直接替换为\0
				}else {
					str[--i] = '\n';
					++count;
				}
			}
		}
		cout << count + 1 << endl;
		cout << str;
	}
	return 0;
}

总结:该方法是利用C++输出遇到'\n''\0'会自动换行.

方法二(推介):以空格和双引号为间隔,统计参数个数,对于双引号,通过添加flag,保证双引号中的空格被输出

#include <iostream>
#include <string>
using namespace std;
int main(){
	string str;
	while (getline(cin, str)){
		int count = 0;
		// 首先计算参数数量
		for (size_t i = 0; i < str.size(); i++){
			if (str[i] == ' ')
				count++;
			// 如果是双引号,向后遍历,直到下一个双引号结束
			if (str[i] == '"'){
				do{
					i++;
				} while (str[i] != '"');
			}
		}
		// 以空格计算个数,空格数量比参数个数少 1
		cout << count + 1 << endl;
		// 用 flag 表示是否包含双引号, 0 表示有双引号
		// 双引号中的空格要打印出来
		// 用异或改变 flag 的值,两个双引号可以使 flag 复原
		int flag = 1;
		for (size_t i = 0; i < str.size(); i++){
			//有双引号,flag通过异或变为0,下一次再遇到双引号,flag置为1
			if (str[i] == '"')
				flag ^= 1;
			// 双引号和普通空格不打印
			if (str[i] != ' ' && str[i] != '"')
				cout << str[i];
			// 双引号中的空格要打印
			if (str[i] == ' ' && (!flag))
				cout << str[i];
			// 遇到双引号之外的空格就换行
			if (str[i] == ' ' && flag)
				cout << endl;
		}
		cout << endl;
	}
	return 0;
}
2 、跳石板

小易来到了一条石板路前,每块石板上从1挨着编号为:1、2、3…这条石板路要根据特殊的规则才能前进:对于小易当前所在的编号为K的 石板,小易单次只能往前跳K的一个约数(不含1和K)步,即跳到 K+X 的位置。

小易当前处在编号为 N 的石板,他想跳到编号恰好为M的石板去,小易想知道最少需要跳跃几次可以到达

例如:N = 4,M = 24
跳跃次数为:4->6->8->12->18->24

于是小易最少需要跳跃5次,就可以从4号石板跳到24号石板

输入描述:输入为一行,有两个整数N,M,以空格隔开。 (4 ≤ N ≤ 100000) (N ≤ M ≤ 100000)
输出描述:输出小易最少需要跳跃的步数,如果不能到达输出 -1
输入:4     24
输出:5

思路:动态规划

初始化大小为 M+1 数组 stepNum,把下标4的位置赋值1,其余都为0;

stepNum[i] 代表的是,从初始位置到 i的步数+1,加1可以不用管,因为最后减去1就行了.
在这里插入图片描述
每到达一个步数,计算可以走的次数,比如走到了 15 的位置,把可以走的步数 [3,5] 保存到一个容器中,然后遍历.

转义方程:伪代码,i为当前位置,divNum[j]为可以走的距离,divNum[j]+i 代表可以到达的位置.

if divNum[j] + i <= M
	if stepNum[divNum[j] + i] != 0
		stepNum[divNum[j] + i] = min(stepNum[divNum[j] + i], stepNum[i] + 1);
	else
		stepNum[divNum[j] + i] = stepNum[i] + 1;

参考代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 计算约数,求除了 1 和本身的约数
void divisorNum(int n, vector<int> &divNum){
	for (int i = 2; i <= sqrt(n); i++){
		if (n%i == 0){
			divNum.push_back(i);
			//平方数的另一个数不加入
			if (n / i != i){
				divNum.push_back(n / i);
			}
		}
	}
}

int Jump(int N, int M){
	// 储存的到达此 stepNum 点的步数,初始 N 为 1 步,从 N 到 N 为 1 步
	vector<int> stepNum(M + 1, 0);
	stepNum[N] = 1;
	for (int i = N; i < M; i++){
		//N 的所有约数,即为从本身这个点开始能走的数量
		vector<int> divNum;
		//0 代表这个点不能到
		if (stepNum[i] == 0)
			continue;
		//求出所有能走的步数储存在divNum的容器中
		divisorNum(i, divNum);
		for (int j = 0; j < divNum.size(); j++){
			// 由位置 i 出发能到达的点为 stepNum[divNum[j]+i]
			if ((divNum[j] + i) <= M && stepNum[divNum[j] + i] != 0)
				stepNum[divNum[j] + i] = min(stepNum[divNum[j] + i], stepNum[i] + 1);
			else if ((divNum[j] + i) <= M)
				stepNum[divNum[j] + i] = stepNum[i] + 1;
		}
	}
	if (stepNum[M] == 0)
		return -1;
	else
		//初始化时多给了一步,故需要减 1
		return stepNum[M] - 1;
}
int main(){
	int n, m;
	cin >> n >> m;
	cout << Jump(n, m) << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序猿的温柔香

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

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

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

打赏作者

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

抵扣说明:

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

余额充值