全排列
题目描述
对1~ n 的 n个整数进行全排列(n<=9),编写程序输出所有可能的排列组合. 用递归算法实现.
输入
输入n
输出
输入n个整数的所有可能的排列组合.
样例输入
3
样例输出
1 2 3
1 3 2
2 1 3
2 3 1
3 2 1
3 1 2
//全排列生成器
#include <iostream>
#include <vector>
#include<iomanip>
using namespace std;
void permute(vector<int>& nums, int start, vector<vector<int>>& result)
{
if (start == nums.size() - 1)
{
result.push_back(nums);
return;
}
for (int i = start; i < nums.size(); ++i)
{
swap(nums[start], nums[i]);
permute(nums, start + 1, result);
swap(nums[start], nums[i]); // 回溯
}
}
void printPermutations(const vector<vector<int>>& result)
{
for (const auto& perm : result)
{
for (int num : perm)
{
cout << setw(4) << num;
}
cout << endl;
}
}
int main()
{
int n;
cin >> n;
if (n < 1 || n > 9)
{
return 1;
}
vector<int> nums(n);
for (int i = 0; i < n; ++i)
{
nums[i] = i + 1;
}
vector<std::vector<int>> result;
permute(nums, 0, result);
printPermutations(result);
return 0;
}
提取整数
题目描述
输入一个字符串,内有数字和非数字字符,例如:
A123x456 17960?302tab5876
将其中连续的数字作为一个整数,依次存放到一数组a中。例如,123放在a[0],456放在a[1]…统计共有多少个整数,并输出这些数。要求用指针方法处理。
输入格式
输入字符串,内有数字和非数字字符, 字符串长度不会超过1024。
输出格式
输出提取的整数,输出一共有多少个整数。
样例输入
A123x456 17960?302tab5876
样例输出
123
456
17960
302
5876
5 integers in total
//从字符串中提取整数
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void extractIntegers(const string& input, vector<int>& numbers)
{
int num = 0; // 用于存储当前正在累加的数字
bool inNumber = false; // 标记当前是否在处理一个数字
for (const char* p = input.c_str(); *p != '\0'; ++p)
{
if (*p >= '0' && *p <= '9')
{ // 检查当前字符是否为数字
num = num * 10 + (*p - '0'); // 将当前字符转换为数字并累加到num
inNumber = true; // 设置标志表示正在处理数字
}
else
{
if (inNumber)
{ // 如果之前在处理数字但现在遇到了非数字字符
numbers.push_back(num); // 将累加的数字存入向量
num = 0; // 重置num以便处理下一个数字
inNumber = false; // 清除标志
}
}
}
// 检查最后一个字符是否是数字,如果是,则将其存入向量
if (inNumber)
{
numbers.push_back(num);
}
}
int main()
{
string input;
cout << "请输入一个字符串: ";
getline(std::cin, input);
vector<int> numbers;
extractIntegers(input, numbers);
for (int num : numbers)
{
cout << num << std::endl;
}
cout << numbers.size() << " integers in total" << endl;
return 0;
}
详细解析一下这个递归函数 permute
的作用和工作原理。
函数签名
void permute(vector<int>& nums, int start, vector<vector<int>>& result)
vector<int>& nums
: 这是要进行全排列的整数向量。使用引用传递,以避免复制整个向量。int start
: 当前处理的起始索引。初始调用时为0。vector<vector<int>>& result
: 存储所有生成的排列组合的结果集。使用引用传递,以便在递归过程中累积结果。
函数逻辑
-
基准条件
if (start == nums.size() - 1) { result.push_back(nums); return; }
- 当
start
达到nums.size() - 1
时,表示当前排列已经完成(即从start
到最后一个元素的所有位置都已经确定)。 - 此时,将当前排列
nums
添加到结果集result
中。 - 返回,结束当前递归调用。
- 当
-
递归过程
for (int i = start; i < nums.size(); ++i) { swap(nums[start], nums[i]); permute(nums, start + 1, result); swap(nums[start], nums[i]); // 回溯 }
- 使用一个循环遍历从
start
到nums.size() - 1
的每一个位置i
。 - 在每次迭代中,交换
nums[start]
和nums[i]
,从而尝试不同的排列组合。 - 调用
permute(nums, start + 1, result)
继续处理下一个位置的排列。 - 交换回
nums[start]
和nums[i]
,恢复原状,以便尝试其他可能的排列组合(这就是回溯的过程)。
- 使用一个循环遍历从
具体步骤举例
假设输入 nums = {1, 2, 3}
,我们来逐步分析递归过程:
-
初始调用:
permute({1, 2, 3}, 0, result)
-
第一层递归 (
start = 0
):i = 0
:- 交换
nums[0]
和nums[0]
(不变),得到{1, 2, 3}
- 调用
permute({1, 2, 3}, 1, result)
- 交换
i = 1
:- 交换
nums[0]
和nums[1]
,得到{2, 1, 3}
- 调用
permute({2, 1, 3}, 1, result)
- 交换
i = 2
:- 交换
nums[0]
和nums[2]
,得到{3, 1, 2}
- 调用
permute({3, 1, 2}, 1, result)
- 交换
-
第二层递归 (
start = 1
):- 对于
{1, 2, 3}
:i = 1
:- 交换
nums[1]
和nums[1]
(不变),得到{1, 2, 3}
- 调用
permute({1, 2, 3}, 2, result)
- 交换
i = 2
:- 交换
nums[1]
和nums[2]
,得到{1, 3, 2}
- 调用
permute({1, 3, 2}, 2, result)
- 交换
- 对于
{2, 1, 3}
:i = 1
:- 交换
nums[1]
和nums[1]
(不变),得到{2, 1, 3}
- 调用
permute({2, 1, 3}, 2, result)
- 交换
i = 2
:- 交换
nums[1]
和nums[2]
,得到{2, 3, 1}
- 调用
permute({2, 3, 1}, 2, result)
- 交换
- 对于
{3, 1, 2}
:i = 1
:- 交换
nums[1]
和nums[1]
(不变),得到{3, 1, 2}
- 调用
permute({3, 1, 2}, 2, result)
- 交换
i = 2
:- 交换
nums[1]
和nums[2]
,得到{3, 2, 1}
- 调用
permute({3, 2, 1}, 2, result)
- 交换
- 对于
-
第三层递归 (
start = 2
):- 对于
{1, 2, 3}
:i = 2
:- 交换
nums[2]
和nums[2]
(不变),得到{1, 2, 3}
- 基准条件满足,将
{1, 2, 3}
添加到result
- 交换
- 对于
{1, 3, 2}
:i = 2
:- 交换
nums[2]
和nums[2]
(不变),得到{1, 3, 2}
- 基准条件满足,将
{1, 3, 2}
添加到result
- 交换
- 对于
{2, 1, 3}
:i = 2
:- 交换
nums[2]
和nums[2]
(不变),得到{2, 1, 3}
- 基准条件满足,将
{2, 1, 3}
添加到result
- 交换
- 对于
{2, 3, 1}
:i = 2
:- 交换
nums[2]
和nums[2]
(不变),得到{2, 3, 1}
- 基准条件满足,将
{2, 3, 1}
添加到result
- 交换
- 对于
{3, 1, 2}
:i = 2
:- 交换
nums[2]
和nums[2]
(不变),得到{3, 1, 2}
- 基准条件满足,将
{3, 1, 2}
添加到result
- 交换
- 对于
{3, 2, 1}
:i = 2
:- 交换
nums[2]
和nums[2]
(不变),得到{3, 2, 1}
- 基准条件满足,将
{3, 2, 1}
添加到result
- 交换
- 对于
总结
- 递归思想: 通过不断固定当前位置的元素,并递归地对剩余部分进行全排列,最终生成所有可能的排列组合。
- 回溯机制: 在每次递归返回后,恢复之前的状态(即撤销交换操作),以便尝试其他可能的排列组合。
- 基准条件: 当
start
达到nums.size() - 1
时,表示一个完整的排列已完成,将其添加到结果集中。
负数计算
Create a structure named Complex to represent a complex number with real and imaginary parts. Write a C/C++ program to add, substrate, multiply, or divide two complex numbers.
Input:
Input two complex numbers using format a+bi, and your operation option.
Output:
Output the operation result of two complex numbers.
Sample Input & Output:
Input first Complex Number using format a+bi
3.5+8.6i
Input second Complex Number using format a+bi
-8.8-10.8i
Input your operation option of the Complex Number: + , - , * , or /
+
The result of two Complex Numbers after + operation is:
-5.30-2.20i
#include <iostream>
#include <sstream>
using namespace std;
struct Complex {
double real;
double imag;
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
friend ostream& operator<<(ostream& os, const Complex& c) {
os << c.real << (c.imag >= 0 ? "+" : "") << c.imag << "i";
return os;
}
};
Complex parseComplex(const string& str) {
double real, imag;
char op;
istringstream iss(str);
iss >> real >> op >> imag;
if (op == '-') imag = -imag;
return Complex(real, imag);
}
Complex add(const Complex& c1, const Complex& c2) {
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
Complex subtract(const Complex& c1, const Complex& c2) {
return Complex(c1.real - c2.real, c1.imag - c2.imag);
}
Complex multiply(const Complex& c1, const Complex& c2) {
return Complex(c1.real * c2.real - c1.imag * c2.imag, c1.real * c2.imag + c1.imag * c2.real);
}
Complex divide(const Complex& c1, const Complex& c2) {
double denominator = c2.real * c2.real + c2.imag * c2.imag;
return Complex((c1.real * c2.real + c1.imag * c2.imag) / denominator,
(c1.imag * c2.real - c1.real * c2.imag) / denominator);
}
int main() {
string input1, input2, operation;
cout << "Input first Complex Number using format a+bi\n";
getline(cin, input1);
Complex c1 = parseComplex(input1);
cout << "Input second Complex Number using format a+bi\n";
getline(cin, input2);
Complex c2 = parseComplex(input2);
cout << "Input your operation option of the Complex Number: + , - , * , or /\n";
cin >> operation;
Complex result;
if (operation == "+") {
result = add(c1, c2);
} else if (operation == "-") {
result = subtract(c1, c2);
} else if (operation == "*") {
result = multiply(c1, c2);
} else if (operation == "/") {
result = divide(c1, c2);
} else {
cerr << "Invalid operation\n";
return 1;
}
cout << "The result of two Complex Numbers after " << operation << " operation is:\n" << result << endl;
return 0;
}
结构体定义
struct Complex {
double real;
double imag;
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
friend ostream& operator<<(ostream& os, const Complex& c) {
os << c.real << (c.imag >= 0 ? "+" : "") << c.imag << "i";
return os;
}
};
成员变量
double real;
double imag;
double real;
: 表示复数的实部。double imag;
: 表示复数的虚部。
构造函数
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
Complex(double r = 0.0, double i = 0.0)
: 这是一个带默认参数的构造函数。它可以接受两个参数r
和i
,分别用于初始化real
和imag
。- 如果没有提供参数,则使用默认值
0.0
。 - 使用成员初始化列表
: real(r), imag(i)
来初始化成员变量real
和imag
。
- 如果没有提供参数,则使用默认值
- 示例:
Complex c1;
创建一个复数对象c1
,其real
和imag
都为0.0
。Complex c2(3.5, 8.6);
创建一个复数对象c2
,其real
为3.5
,imag
为8.6
。
友元运算符重载
friend ostream& operator<<(ostream& os, const Complex& c) {
os << c.real << (c.imag >= 0 ? "+" : "") << c.imag << "i";
return os;
}
friend ostream& operator<<(ostream& os, const Complex& c)
: 这是一个友元函数,通过重载输出流运算符<<
,使得可以直接使用cout
输出Complex
对象,并按照标准格式(如a+bi
或a-bi
)显示复数。friend
关键字: 声明该函数为Complex
的友元函数,使其可以访问Complex
的私有和保护成员。ostream& operator<<(ostream& os, const Complex& c)
: 定义了一个名为operator<<
的函数,它接受两个参数:ostream& os
: 输出流对象,通常是cout
。const Complex& c
: 要输出的Complex
对象。
- 函数体:
os << c.real
: 将Complex
对象的实部real
输出到流os
。(c.imag >= 0 ? "+" : "")
: 使用三元运算符来决定是否输出加号+
。如果虚部imag
大于或等于0
,则输出一个加号+
;否则不输出任何符号。例如,如果c.imag
是8.6
,则输出+
。如果c.imag
是-8.6
,则不输出任何符号。<< c.imag << "i"
: 将Complex
对象的虚部imag
和后缀i
输出到流os
。例如,如果c.imag
是8.6
,则输出8.6i
。如果c.imag
是-8.6
,则输出-8.6i
。return os;
: 返回输出流对象os
,以便支持链式调用(例如cout << c1 << c2;
)。
示例:
假设我们有一个 Complex
对象 c(3.5, 8.6)
,当执行 cout << c;
时,会发生以下步骤:
- 调用重载的
operator<<
函数。 os << c.real
输出3.5
。(c.imag >= 0 ? "+" : "")
判断c.imag
是否大于或等于0
,结果为true
,因此输出+
。<< c.imag << "i"
输出8.6i
。- 最终输出结果为
3.5+8.6i
。
再比如,假设我们有一个 Complex
对象 c(-8.8, -10.8)
,当执行 cout << c;
时,会发生以下步骤:
- 调用重载的
operator<<
函数。 os << c.real
输出-8.8
。(c.imag >= 0 ? "+" : "")
判断c.imag
是否大于或等于0
,结果为false
,因此不输出任何符号。<< c.imag << "i"
输出-10.8i
。- 最终输出结果为
-8.8-10.8i
。
函数定义
Complex parseComplex(const string& str) {}
Complex
: 这是函数的返回类型。函数将解析输入字符串并返回一个Complex
类型的对象。parseComplex
: 这是函数的名称,表示该函数用于解析复数字符串。(const string& str)
: 这是函数的参数列表。它接受一个常量引用类型的string
参数str
,表示要解析的复数字符串。
变量声明
double real, imag;
char op;
double real, imag;
: 声明两个双精度浮点数变量real
和imag
,分别用于存储复数的实部和虚部。char op;
: 声明一个字符变量op
,用于存储操作符(通常是'+'
或'-'
)。
字符串流初始化
istringstream iss(str);
istringstream iss(str);
: 创建一个istringstream
对象iss
,并将传入的字符串str
初始化给它。istringstream
是 C++ 标准库中的一个类,用于从字符串中读取数据,类似于从文件或标准输入读取数据的方式。
解析字符串
iss >> real >> op >> imag;
iss >> real >> op >> imag;
: 使用>>
操作符从iss
中依次提取实部real
、操作符op
和虚部imag
。- 例如,如果输入字符串是
"3.5+8.6i"
,则:real
将被赋值为3.5
op
将被赋值为'+'
imag
将被赋值为8.6
- 例如,如果输入字符串是
处理负虚部
if (op == '-') imag = -imag;
if (op == '-') imag = -imag;
: 如果操作符op
是'-'
,则将虚部imag
取反。- 继续上面的例子,如果输入字符串是
"-8.8-10.8i"
,则:real
将被赋值为-8.8
op
将被赋值为'-'
imag
将被赋值为10.8
(因为op
是'-'
,所以imag
被取反)
- 继续上面的例子,如果输入字符串是
返回结果
return Complex(real, imag);
}
return Complex(real, imag);
: 使用解析得到的实部real
和虚部imag
构造一个新的Complex
对象,并将其返回。
学习收益计算
题目描述
期末考试一共有 𝑛门课程,huaijiao 需要在有限时间内尽可能合理地安排速通计划,来让自己的绩点最大化。
已知第 𝑖门课程的学分 𝑐𝑖和难度系数 𝑘𝑖,huaijiao 如果花费 𝑥天来速通第 𝑖门课程,那么他将取得的成绩 𝑤𝑖=min(1,𝑥/𝑘𝑖)×100。
huaijiao 最后的总成绩 𝑊=∑𝑖=1𝑛(𝑤𝑖×𝑐𝑖)。
huaijiao 现在一点没学,距离期末考试只剩下 𝑀M 天,每天 huaijiao 只能专心学一门,请你帮帮他最大化他的总成绩。
输入格式
第一行两个正整数 𝑛,𝑀分别表示总共有多少门课程和距离期末考试还剩下多少天。
第二行 𝑛个正整数,表示每门课程的学分:𝑐1,𝑐2,⋯ ,𝑐𝑛。
第三行 𝑛个正整数,表示每门课程的难度:𝑘1,𝑘2,⋯ ,𝑘𝑛。
输出格式
输出一个实数,保留四位小数,表示最大的总成绩。
输入输出样例
输入 #1
4 8 2 1 10 10 4 9 5 2
输出 #1
2050.0000
输入 #2
10 19 1 10 3 3 1 10 2 7 8 6 3 2 1 2 7 9 7 6 4 7
输出 #2
3544.4444
输入 #3
8 18 4 6 4 1 3 8 9 3 6 9 6 7 5 9 8 3
输出 #3
1822.2222
代码
#include<iostream>
#include<vector>
#include<iomanip>
#include<algorithm>
using namespace std;
struct Course
{
int c;
int k;
double unitvalue;//单位时间的收益。学分除以难度系数
};
//按单位时间收益从高到底排序课程
bool comparevalue(const Course& a, const Course& b)
{
return a.unitvalue > b.unitvalue;
}
int main()
{
int n, M;
cin >> n >> M;
vector<Course>course(n);
for (int i = 0; i < n; i++)
{
cin >> course[i].c;
}
for (int i = 0; i < n; i++)
{
cin >> course[i].k;
course[i].unitvalue = static_cast<double>(course[i].c) / course[i].k;
}
//按单位时间收益的大小排序,排序的依据是comparevalue函数
sort(course.begin(), course.end(), comparevalue);
double totalsorce = 0.0;
for (int i = 0; i < n; i++)
{
if (M == 0)break;
double studydays = min(M, static_cast<int>(course[i].k));
double sorce = min(1.0, studydays / course[i].k) * 100.0 * course[i].c;
totalsorce += sorce;
M -= studydays;
}
cout << fixed << setprecision(4) << totalsorce << endl;
return 0;
}