C++程序设计ACM题库总结
知识点总结
串流
istringstream类用于执行C++风格的串流的 输入操作。
ostringstream类用于执行C风格的串流的 输出 操作。
strstream类同时可以支持C风格的串流的 输入输出 操作。
istringstream的构造函数原形如下:istringstream::istringstream(string str);它的作用是从string对象str中读取字符,以空格为分隔符。
这三个类使用时要加头文件<sstream>
// 举例:
#include<sstream>
for (string s; getline(cin, s);) {
istringstream sin(s); // 从s中读取字符,以空格为分隔符。
for (sin >> b; sin >> op >> c;) { // 读到各个变量中
……
}
}
// 又举例:
#include<sstream>
string str="i am a boy";
istringstream is(str); 从str中读取字符,以空格为分隔符。
string s;
while(is>>s) {
cout<<s<<endl;
}
/*
输出是:
i
am
a
boy
*/
log
c++中的log函数:
引入头文件<cmath>
以e为底:log(n)
以10为底:log10(n)
// 以m为底:log(n)/log(m)
ceil
格式: double ceil(double x); //参数为double!
作用: 返回 大于等于x 的最小整数
引入头文件<cmath>
举例:
#include<cmath>
double number = 123.54;
cout<< floor(number); // 123.00
cout<< ceil(number); //124.00
count
格式:count (Iterator first, Iterator last, const T& value );
作用:返回[first,last)范围内值为value的元素个数。可用于字符串、数组、容器等。
//举例:
string c1;
int cl = count(s1.begin(), s1.end(), '1');
// 又举例:
有一个数组a[];a是数组的首地址
num = count(a + 1, a + 5, 0);
reverse
功能:反转在[first,last)范围内的顺序,无返回值。多用于字符串、数组、容器。
头文件是 <algorithm>
string的各种操作
要加头文件<string>
初始化
string(int n, char c); //使用 n 个字符 c 初始化
如:string str(5,'0');
substr
string substr(int pos = 0, int n = npos) const;
返回 由 pos 开始的 n 个字符 组成的字符串;若只有一个数字pos,表示从该数字到结尾。
如:
string s("123456789");
cout<<s.substr(0, 5); // 输出12345
cout<<s.substr(3); // 输出 456789
string::find 返回第一次出现的位置
格式:
size_t find ( const string& str, size_t pos = 0 );
size_t find ( const char* s, size_t pos = 0 );
size_t find ( char c, size_t pos = 0 );
作用:
返回 str/s/c 在字符串中第一次出现的位置。如果没有找到,那么会返回一个特别的标记npos。(返回值可以看成是一个int型的数)
如果指定了Pos,则查找只在Pos之后的位置进行。
//举例:
string str ("There are two needles in this haystack.");
size_t found = str.find("needle")
if (found != string::npos)
cout << int(found) << endl;// 14
string::find_first_of
格式:
size_t find_first_of ( const string& str, size_t pos = 0 );
size_t find_first_of ( const char* s, size_t pos = 0 );
size_t find_first_of ( char c, size_t pos = 0 );
作用:
在字符串中查找一个或多个字符,该一个或多个字符只需要是 str/s/c 内容的一部分即可, 并返回该内容在字符串中第一次出现的位置。
如果指定了Pos,则查找只在Pos之后的位置进行。
//举例:
string str ("Replace the vowels in this sentence by asterisks.");
size_t found = str.find_first_of("aeiou");
while (found!=string::npos) {
str[found]='*';
found = str.find_first_of("aeiou",found + 1);
}
cout << str << endl;
// 输出:R*pl*c* th* v*w*ls *n th*s s*nt*nc* by *st*r*sks.
string::find_first_not_of
格式:
size_t find_first_not_of ( const string& str, size_t pos = 0 );
size_t find_first_not_of ( const char* s, size_t pos = 0 );
size_t find_first_not_of ( char c, size_t pos = 0 );
作用:
在字符串中查找一个或多个字符,要求该一个或多个字符不是 str/s/c 内容的一部分, 并返回该内容在字符串中第一次出现的位置。
如果指定了Pos,则查找只在Pos之后的位置进行。
//例子:
string str (“look for non-alphabetic characters...”);
size_t found = str.find_first_not_of("abcdefghijklmnopqrstuvwxyz ");
if (found != string::npos) {
cout << "str里第一个非字母的字符是" << str[found]; // -
cout << "这个字符的位置:" << int(found) << endl; // 12
}
return 0;
}
string::find_last_of 返回最后一次出现的位置
格式:
size_t find_last_of ( const string& str, size_t pos = 0 );
size_t find_last_of ( const char* s, size_t pos = 0 );
size_t find_last_of ( char c, size_t pos = 0 );
作用:
从字符串的最后开始查找,查找一个或多个字符,该一个或多个字符只需要是str, s 或 c内容的一部分即可, 并返回该内容在字符串中最后一次出现的位置。
如果指定了Pos,则查找只在Pos之前的位置进行,之后的内容忽略。
string::find_last_not_of
格式:
size_t find_last_not_of ( const string& str, size_t pos = 0 );
size_t find_last_not_of ( const char* s, size_t pos = 0 );
size_t find_last_not_of ( char c, size_t pos = 0 );
作用:
从字符串的最后开始查找,查找一个或多个字符,该一个或多个字符b不是str, s 或 c内容的一部分, 并返回该内容在字符串中最后一次出现的位置。
如果指定了Pos,则查找只在Pos之前的位置进行,之后的内容忽略。
// 例子:
string str ("erase trailing white-spaces \n");
string whitespaces (" \t\f\v\n\r");
size_t found = str.find_last_not_of(whitespaces);
if (found!=string::npos) str.erase(found+1); //删除found后面的内容
cout << str ; //erase trailing white-spaces
return 0;
}
【C++中string.find()函数与string::npos参考文章:https://www.cnblogs.com/zl1991/p/7262130.html】
sort
格式:
void sort (Iterator first, Iterator last);
void sort (Iterator first, Iterator last, Compare comp ); // 自定义排序方式
作用:
将 [first,last) 范围内元素按升序(从小到大)排序。如指定comp参数,元素间按照comp指定排序方式进行排序。
sort自定义排序方式
// 方法一:自定义函数(排列对称串里用到)
bool myComp(const string& s1, const string& s2) {
return s1 <= s2;
}
sort(ss.begin(), ss.end(), myComp); // sort(begin, end, 自定义的函数名);
// 方法二:结构体对象
struct classComp {
bool operator() (string s1, string s2) { // 先按长度再按ASCII码
return s1.length() != s2.length() ? s1.length() < s2.length() : s1 <= s2;
}
} myobject;
sort(ss.begin(), ss.end(), myobject); // sort(begin, end, 自定义的结构体对象名);
//方法三:运算符重载(彼此两点最近、离直线最近的点 2题里用到过)
bool operator<(const Point& a, const Point& b) {
return a.x != b.x ? a.x < b.x : a.y < b.y;
}
map容器
map< key,value >
1. map是一种关联容器,存储的每个对象包含两部分内容:键元素ke和值元素value。键和值类型可以不同。
键元素用于唯一标识对象。用key进行访问和查找value
2. map 的好处是键值自动形成了大小比较的标准 , 无论插入还是删除 , 都是内部自动维护从小到大的顺序 , 因为查找方式用的是二分法查找 , 速度比顺序查找快很多。
3. map需要掌握的方法:
定义:map<int,int> m;
访问:m[3]=100;
删除:m.erase(it); 或者 m.erase(key);
查找:m.find(key) //返回指向查找对象的指针。
set容器
set<元素类型>
set不允许容器中有重复的元素,multiset允许容器中有重复的元素
set/multiset属于关联式容器,底层结构是用二叉树实现,所有元素都会在插入时自动从小到大被排序,因此查找速度比较快。
set需要掌握的方法:
定义:set<int> s;
插入:s.insert(3);
删除:s.erase(it) 或 s.erase(3);
查找:s.find(3); //返回指向查找对象的指针,如果没找到则返回指向end的指针:s.end()
注意set 不能用[i]访问,只能用迭代器访问
set自定义排序方法
// 方法:构建结构体
struct mycomp {
bool operator() (const int& a, const int& b) { // 参数类型与set元素类型要一致
return a < b;
}
};
set<int,mycomp> myset; //定义:set<元素类型,结构体名> myset;
之后 myset.insert(元素) 时自动会排好序
bitset容器
1. Bitset是一种用来存储位bit的容器。每个元素是c++中最小元素类型char型,只占用一位。元素仅包含两个可能的值:0或1 (true或false)。
2. Bitset能够由整型值或由0、1构成的字符串构建,并可以转换成整型值或由0、1构成的字符串。
3. 每个元素可以被单独访问。与数组非常相似。例如:一个bitset类型的mybitset,则mybitset[3]访问bitset中的第四个元素,就像访问数组元素一样。
4. bitset需要掌握的方法:
定义:bitset<4> mybits; bitset<5> mybits(string("01011"));
全部置1:mybits.set(pos,val)
全部置0:mybits.reset(pos,val)
访问:mybits.test(pos)
mybits.set()
定义形式:
bitset<N> set ( );
bitset<N> set ( size_t pos, bool val = true );
作用:
无参数的:将所有元素置1。
pos有参数的:将位置在pos的元素置1。注意是从右往左数!注意是从右往左数!注意是从右往左数!
bitset<4> mybits;
cout << mybits.set() << endl; // 全部置1,1111
cout << mybits.set(2,0) << endl; // 将从右往左数第2个位置置0,1011
cout << mybits.set(2) << endl; // 将从右往左数第2个位置置1,1111
mybits.reset()
定义形式:
bitset<N> reset ( );
bitset<N> reset ( size_t pos ); // 它只有一个参数,就是位置
作用:
无参数的:将所有元素置0。
有参数的:将位置在pos的元素置0。注意是从右往左数!注意是从右往左数!注意是从右往左数!
bitset<4> mybits (string("1111"));
cout << mybits.reset(1) << endl; // 将从右往左数第1个位置置0,1101
cout << mybits.reset() << endl; //全部置0,0000
mybits.test(i)
定义形式:
bool test ( size_t pos );
作用:返回某位的值。位置pos的bit值为1的话返回true,否则返回false。注意是从右往左数!注意是从右往左数!注意是从右往左数!
bitset<5> mybits (string("01011"));
for (size_t i=0; i< mybits.size(); ++i)
cout << mybits.test(i) << endl;
C++格式化输出
//方法:
要加 <iomanip> 头文件
整个数字保留4位:
cout << setprecision(4) << 数字;
或者用:cout.precision(4); cout << 数字;
保留到小数点后4位,就在前面加fixed:
cout << fixed << setprecision(4) << 数字;
输出宽度为 10 字符,默认右对齐:
cout << setw(10) << 数字;
输出宽度为 10 字符,左对齐:
cout << setw(10) << left << 数字;
注意:
fixed,setprecision,scientific只要出现一次,就对以后的数字都起作用;但setw只对它后面的指定数字起作用。
如果要去掉fixed:
cout.unsetf(ios::fixed);
cout << 数字;
举例:
#include <iostream>
#include <iomanip> //注意要包含该头文件
using namespace std;
int main(void)
{
const double value = 12.3456789;
cout << setprecision(4) << value << endl; // 4精度,输出为12.35
cout << setprecision(8) << value << endl; // 8精度,输出为12.345679
// 加了fixed意味着是固定点方式显示,所以这里的精度指的是小数位,输出为12.3457
cout << fixed << setprecision(4) << value << endl;
// 只要不unsetf,fixed和setprecision的作用就一直在,依然显示12.3457
cout << value << endl;
// 输出宽度为 10 字符,默认右对齐,输出为: 12.3457
cout << setw(10) << value << endl;
// 输出宽度为 10 字符,左对齐,输出为:12.3457 12.3457
cout << setw(10) << left << value << value << endl;
// 去掉了fixed,所以精度恢复成整个数值的有效位数,显示为12.35
cout.unsetf(ios::fixed);
cout << value << endl;
// 恢复成原来的样子,输出为12.3457
cout.precision(6);
cout << value << endl;
}
(参考文章:http://c.biancheng.net/view/275.html)
判断素数的方法
// 从 2 到 n开方 均整除判断(原因:一个合数一定含有小于它平方根的质因子。)
bool IsPrime(int N) {
for (int i = 2; i <= sqrt(N); i++)
if (N % i == 0) return false; // 说明是合数不是素数
return true;
}
一
1190 列出完数
题目:
列出完数
Time Limit:1000MS Memory Limit:32768K
Description:
自然数中,完数寥若晨星,请在从1到某个整数范围中打印出所有的完数来。 所谓“完数”是指一个数恰好等于它的所有不同因子之和。例如,6是完数,因为6=1+2+3。而24不是完数,因为24≠1+2+3+4+6+8+12=36。
Input:
输入数据中含有一些整数n(1<n<=10000)。
Output:
对于每个整数n,输出所有不大于n的完数。每个整数n的输出由n引导,跟上冒号,然后是由空格开道的一个个完数,每个n的完数列表应占独立的一行。
Sample Input:
100
5000
Sample Output:
100: 6 28
5000: 6 28 496
Source:
qianneng
核心思想:
先把所有小于 10000 的完数计算出来,放在向量中,然后对每个 n ,只需在向量中打印每个完数 , 直到 n 之内的完数的界即可。
代码:
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> v;
for (int i = 2; i < 10000; i += 2){ //构造10000之内的完数表 // 推测完数为偶数 , 故步长为 2
int sum = 1;
for (int j = 2; j <= i / 2; j++) // 判断每个小于 m 的整数是否能整除 m
if (i % j == 0)
sum += j; // 累计 m 的因子和
if (sum == i) // 比较因子和与 m
v.push_back(i);
}
for (int n; cin >> n;){ //循环输入数据
cout << n << ":";
for (int j = 0; v[j] <= n; j++)
cout << " " << v[j];
cout << endl;
}
return 0;
}
1191 12!配对
题目:
12!配对
Time Limit:1000MS Memory Limit:32768K
Description:
找出输入数据中所有两两相乘的积为12!的个数。
Input:
输入数据中含有一些整数n(1≤n<2^32)。
Output:
输出所有两两相乘的积为12!的个数。
Sample Input:
1 10000 159667200 9696 38373635
1000000 479001600 3
Sample Output:
2
Source:
qianneng
流程:
1. 建立一个向量存放输入数据
2. 在输入数据的时候 , 可以过滤那些不是 12 ! 的因子的数进行 set 集合 , 以提高查找的效率 .
3. 在输入数据还没有进集合之前先对集合元素进行配对查找 , 若找到 , 就可以只删除集合中的对应元素,若没有找到,就进集合。因为向量不擅长中间元素的删除操作 , 这可以用 set 来做 , set 的查找速度比较快 , 在这里正好需要大量的查找工作 .
代码:
#include<iostream>
#include<set>
using namespace std;
int main(){
int num = 0;
int f12 = 479001600;//12!
multiset<int> s;
for (int n; cin >> n;) { //循环输入数据
if (f12 % n == 0) { //如果这个数是12!的因子
//在输入数据还没进入集合前先对集合元素进行配对查找
set<int>::iterator it = s.find(f12 / n);
if (it != s.end()){ //若找到对应元素,删除集合中的对应元素
num++;
s.erase(it);
}
else{ //若没有找到对应元素,该元素进集合
s.insert(n);
}
}
}
cout << num << endl;//输入数据中所有两两相乘的积为12!的个数
return 0;
}
1192 整数的因子数
题目:
整数的因子数
Time Limit:2000MS Memory Limit:32768K
Description:
找出整数的所有因子数。 一个整数n的因子数为包含它自身的所有因子的个数。例如:12的因子数为6(1,2,3,4,6,12)。
Input:
输入数据中含有一些整数n(1≤n<2^32)。
Output:
对于每个n,列出其所有因子数,每个n加上冒号单独列一行。
Sample Input:
11 22 33 24
Sample Output:
11: 2
22: 4
33: 4
24: 8
Source:
qianneng
*/
知识点:
1. 数学定理的应用:一个整数的因子数等于其每个素因子的个数加一之后的乘积
2. 判断素数最有效的方法:从2到sqrt(n)整除判断
3. map
核心思想:
1. 一个整数的因子数等于其每个素因子的个数加一之后的乘积
注意:超过其平方根值的素因子最多只有一个。
/*
例如:12=2×2×3,2和3就是12的素因子。
2有2个,3有1个,
12的因子数就是(2+1)+(1+1)=5个,
再看12的因子有:1、2、3、4、6,确是5个。
*/
2. 使用容器 map。
流程:
main():
1. 构造素数表;
2. 对于每个整数,遍历素数表,构造该整数的素因子表;
3. 遍历该整数的素因子表,该个整数的因子数等于它的所有素因子个数加1之后的乘积;
4. 输出该个整数的因子数。
代码:
// 一般方法
#include<iostream>
#include<map>
using namespace std;
int main()
{
map<int, int> m;
for (int n; cin >> n;)//输入
{
int num = 2;
for (int i = 2; i <= n / 2; i++)
if (n % i == 0) num++;
m.insert(make_pair(n, num));
}
for (auto num : m) //输出
cout << num.first << ":" << num.second << endl;
return 0;
}
// 改进方法
// 核心思想:一个整数的因子数等于其每个素因子的个数加一之后的乘积
#include<iostream>
#include<map>
#define MAX 6600
using namespace std;
// 判断素数
// 从2到n开方均整除判断(原因:一个合数一定含有小于它平方根的质因子。)
bool IsPrime(int N) {
for (int i = 2; i <= sqrt(N); i++)
if (N % i == 0) return false;
return true;
}
int main()
{
//构造2^16以内的素数表。
/* 因为输入的整数n(1≤n<2^32)),而超过其平方根值的素因子最多只有一个,
所以只需要到平方根就行了,超过平方根的最后再做判断*/
int W[MAX]; // 素数表
int Size = 0;
for (int i = 2; i < (1 << 16); i++)
if (IsPrime(i)) W[Size++] = i;
int N;
while (cin >> N)
{
//对于每个整数,遍历素数表,构造该整数的素因子表
int Tmp = N;
map<int, int> Map; // 该整数的素因子表(主键存放素因子,值存放分解个数)
for (int i = 0; Tmp != 1 && i < Size; )
{
if (Tmp % W[i] == 0) // 如果W[i]是这个整数的素因子
{
Map[W[i]]++;
Tmp /= W[i];
}
else
i++;
// 或者直接 Map[W[i]] = Tmp / W[i]; Tmp /= W[i]; i++; ?
}
// 最后除完后得到的Tmp也是一个素因子,判断Tmp是否大于2^16,如果大于2^16就加到素因子表里。
if (Tmp > (1 << 16)) Map[Tmp]++;
//遍历该整数的素因子表,该个整数的因子数等于它的所有素因子个数加1之后的乘积
int Sum = 1;
for (map<int, int>::iterator It = Map.begin(); It != Map.end(); It++)
Sum *= (1 + It->second);
//输出该个整数的因子数
cout << N << ":" << Sum << endl;
}
return 0;
}
1193 浮点数的位码
题目:
浮点数的位码
Time Limit:1000MS Memory Limit:32768K
Description:
长双精度型是C++语言内部表达能力最强的数据类型。研究其内部的位码也是饶有兴趣的。针对每个长双精度数,输出其位码。
Input:
输入数据中含有一些浮点数n(-3.4×10^4932<n<1.1×10^4932)。
Output:
对于每个n,列出其位码,每个位码8位一组,中间用逗号隔开,每5组成一行,每个位码列成两行,位码之间空出一行,见样本输出。
Sample Input:
15.6756
12345.67891023456
Sample Output:
00000000,01110000,11010111,00010010,11110010
01000001,11001111,11111010,00000010,01000000
00000000,01101000,10011001,00111110,00110100
10110111,11100110,11000000,00001100,01000000
Hint:
浮点内码各编译器实现不同.本题用BCB测试数据,故请用BCB提交.
Source:
qianneng
核心思想:
1. 长双精度型数的长度为 10 个字节 , 这可以从 sizeof ( long double ) 中得到。
2. 因为长双精度数是浮点数,没有移位操作,所以要将其转换成整型,转换为指针,为了对应 8 位一个单元的输出,将其转换为字符指针。
3. 输出:
每次打印一个长双精度数之前确定是否是空行。(用一个m计数。
一共是 10 个字节 , 所以打印一个长双精度数要循环 10 次;每个"字节"再循环 8 次;
输出一个 8 位后要查看是打印逗号还是回车。(用i判断。
代码:
#include<iostream>
using namespace std;
int main()
{
int m = 0;
for (long double d; cin >> d;)
{
cout << (m++ ? "\n" : ""); // 每打印一个长双精度数之前确定是否空行
char* p = (char*)&d; //转换为字符指针
for (int i = 0; i < 10; ++i) //长双精度占10个字节,循环10次
{
for (int j = 7; j >= 0; --j) //每个字节存储8位无符号数,循环8次
{
cout << (*(p + i) >> j & 1); //判断右移j位后,最右边的一位是不是1
}
cout << (i % 5 == 4 ? "\n" : ","); //输出一个8位后查看是打印逗号还是回车
// 如 i=0,1,2,3,5,6,7,8时打印逗号,i=4和9时打印回车
}
}
return 0;
}
1194 对称素数
题目:
对称素数
Time Limit:1000MS Memory Limit:32768K
Description:
统计输入的整数中有多少个对称素数。
Input:
输入数据中含有一些随机整数n(1<n<10^8)。
Output:
统计一共读入了多少个对称素数,并予以输出。
Sample Input:
15
11 313
Sample Output:
2
Source:
qianneng
核心思想:
1. 偶数位的对称数一定是合数。//如1221是合数。
2. 先建立10^7内的素数表,采用位集 bitset 筛法是一种快捷的方式。
3. 为了节省时间,通过建立素数表和对称数表,进行交集操作得到一个对称素数表。
流程:
1. 建立素数表:
排除8位的数字,建10^7以内的素数表p;p里有10^7位,每一位代表该位数字是否是素数,p[i]=1 说明 i 是素数;
把是2的倍数的位置 置0;
把素数的倍数的位置 置0
2. 建立对称数表:
先将个别 1 位数、2 位数对称素数放入位集中;
再找3、5、7位的对称数置1;
3. 取交集;
4. 读入整数 , 只要该整数大于 10^7 , 则直接判断为非对称素数。如果小于 10^7 , 则通过查表可以确定 true 或 false。
/*
在简单的编程运行之后 , 发现 10 ^7 ( 或 10 ^8 ) 之内的对称素数总量也不过 651 个 , 因此 , 完全可以采用集合 set 来存放对称素数表 , 以达到与位集同样的性能 .
因为集合的搜索是二分查找 , 性能丝毫不输于位集 . 而且 , 在建立对称素数表过程中 , 还可以省略交集操作。
这种方法之后再写吧。
*/
代码:
#include<iostream>
#include<bitset>
using namespace std;
bitset<10000000> p, q; //全局位集 // 共有10^7个数
void sieve(){ // 建一次性素数表p
p.set(); // 10^7 位全置 1
for (int i = 4; i < 10000000; i += 2) // 清除 2 的倍数位(置0)
p.reset(i);
for (int i = 3; i < 3163; i += 2) // 清除已是素数的倍数位 ??
if (p.test(i))
for (int j = i * i; j < p.size(); j += i * 2) {
p.reset(j);
}
}
void sym() { // 建立非完全对称数表q ??
q.set(2); q.set(3); q.set(5); q.set(7); q.set(11); // 先将个别 1 位数、2 位数对称素数放入位集中
for (int i = 1; i <= 999; i += 2) { // 再找3、5、7位的对称数
for (int j = 0, k,m; j <= 9; ++j) {
k = 10 * (i >= 10 ? 10 : 1) * (i >= 100 ? 10 : 1);
m = (10 * k + 1) * i + k * j + (i < 10 ? 0 : (i < 100 ? 9 * (i % 10 - i / 10) : 99 * (i % 10 - i / 100)));
q.set(m);
}
}
}
int main(){
sieve(); // 建一次性素数表
sym(); // 建立非完全对称数表
q &= p; // 取交集
int num = 0;
for (int a; cin >> a; )
if (a < 10000000) num += q.test(a);
cout << num << "\n";
return 0;
}
//其他方法
#include<iostream>
#include<string>
#include<sstream>
#include<math.h>
using namespace std;
bool isPrime(int n)
{
int i;
if (n == 1)
{
return false;
}
if (n != 2 && n % 2 == 0)
return false;
for (int i = 3; i * i < n; i++)
{
if (n % i == 0)
return false;
}
return true;
}
int main()
{
int x; int sum = 0;
while (cin >> x)
{
if (isPrime(x))
{
string s;
stringstream ss;
ss << x;
ss >> s;
int n = s.length(), flag = 1;
for (int i = 0; i <= n / 2; i++)
{
if (s[i] != s[n - 1 - i])
{
flag = 0;
break;
}
}
if (flag)
sum++;
}
}
cout << sum << endl;
return 0;
}
二
1195 密钥加密
题目:
密钥加密
Time Limit:1000MS Memory Limit:32768K
Description:
密钥加密是将密钥数字串值循环加到明文(需要加密的文字串)上,使得明文变形而不可阅读,变形后的文字串称为密文。
例如,密钥为4972863,明文为“the result of 3 and 2 is not 8”,则循环加密的过程及结果为:
即密文为:
xql"zkvyu "wl#7)hpl&5$rz"vuw$A
这里的密钥加密是循环加密,并且在ASCII码值32(’ ’)到122(’z’)之间做模运算,超过了122的值便依次回跳到32,33,...等值。例如,’t’+7=116+7=123=122+1,其值超过122一个位置,因此回跳到值32。显然,密文也全部是由可见字符所组成。
Input:
输入数据中含有若干组数据,每组数据由密钥和明文组成,密钥和明文均单独占一行。每组数据之间没有空行。
Output:
对于每组数据,对应输出一行密文。
Sample Input:
4972863
the result of 3 and 2 is not 8
123
Hello World
Sample Output:
xql"zkvyu "wl#7)hpl&5$rz"vuw$A
Igomq#Xqumf
Source:
qianneng
代码:
#include<iostream>
#include<string>
using namespace std;
//密钥加密。Key是密钥,mw是明文,返回密文。
string Encode(string Key, string mw) {
string Result; // 密文
for (int i = 0; i < mw.length(); i++) {
char ch = char(mw[i] + Key[i % Key.length()] - '0'); // 明文字符加上对应的密钥数字
if (ch > 122) ch = ch - 91; // 如果超出范围 -122+31
Result += ch;
}
return Result;
}
int main() {
for (string Key, mw; getline(cin, Key) && getline(cin, mw);)
cout << Encode(Key, mw) << endl;
return 0;
}
1203 密钥解密
题目:
密钥解密
Time Limit:1000MS Memory Limit:32768K
Description:
密钥解密是在同一密钥加密的基础上进行解密,也可以看作是加密的反操作。解密是将密文的对应位循环减去密钥数字串值,使得密文变形显露为明文。
例如,用同一密钥”4972863”对密文:
xql"zkvyu "wl#7)hpl&5$rz"vuw$A
进行解密:
即得到明文:
the result of 3 and 2 is not 8
密钥解密也是循环解密,并且在ASCII码值32(’ ’)到122(’z’)之间做模运算,小于32的值,便依次跳跃到122,121,...等值。例如,32-7其值已不足32的7个位置,它应该跳到122-6=116即’t’上。
Input:
输入数据中含有若干组数据,每组数据由密钥和密文组成,密钥和密文均单独占一行。每组数据之间没有空行。
Output:
对于每个数据组,对应输出一行明文。
Sample Input:
4972863
xql"zkvyu "wl#7)hpl&5$rz"vuw$A
123
Igomq#Xqumf
Sample Output:
the result of 3 and 2 is not 8
Hello World
Source:
qianneng
代码:
#include<iostream>
#include<string>
using namespace std;
//密钥解密。Key是密钥,mw是密文,返回明文。
string Decode(const string Key, const string mw) {
string Result;
for (int i = 0; i < mw.length(); i++) {
char ch = char(mw[i] - Key[i % Key.length()] + '0');
if (ch < 32) ch = ch + 91;// 如果超出范围 -31+122
Result += ch;
}
return Result;
}
int main() {
for (string Key, mw; getline(cin, Key) && getline(cin, mw);)
cout << Decode(Key, mw) << endl;
return 0;
}
1204 01串排序
题目:
01串排序
Time Limit:1000MS Memory Limit:32768K
Description:
将01串首先按长度排序,长度相同时,按1的个数多少进行排序,1的个数相同时再按ASCII码值排序。
Input:
输入数据中含有一些01串,01串的长度不大于256个字符。
Output:
重新排列01串的顺序。使得串按基本描述的方式排序。
Sample Input:
10011111
00001101
1010101
1
0
1100
Sample Output:
0
1
1100
1010101
00001101
10011111
Source:
qianneng
核心思想:
1. 容器选用:因为需要排序,比较好的办法是采用集合容器 set。考虑到集合中可能会出现相同的元素,可重集 multiset。Multiset可以单独指定比较函数。
2. 排序方式:string 串大小比较的默认标准是按各字符的 ASCII 码大小,如果算法中需要改变大小比较的标准,则要自己设计比较函数。Multiset自定义比较排序方法。
3. STL内置算法:count;
代码:
#include<iostream>
#include<set>
using namespace std;
struct Comp{
bool operator()(const string& s1, const string& s2)const {
//首先按长度排序
if (s1.length() != s2.length())
return s1.length() < s2.length();
//长度相同时,按1的个数多少进行排序,1的个数相同时再按ASCII码值排序。
int cl = count(s1.begin(), s1.end(), '1');
int c2 = count(s2.begin(), s2.end(), '1');
return (cl != c2 ? cl < c2 : s1 < s2);
}
};
int main(){
multiset<string,Comp> me;
multiset<string,Comp> ::iterator It;
for (string s; cin >> s; )
me.insert(s);
for (It it = me.begin(); it != me.end(); ++it)
cout << *it << endl;
return 0;
}
1205 按绩点排名
题目:
按绩点排名
Time Limit:2000MS Memory Limit:32768K
Description:
有一些班级的学生需要按绩点计算并排名。 每门课程的成绩只有在60分以上(含),才予以计算绩点。课程绩点的计算公式为: (课程成绩 – 50) ÷ 10 × 学分数 一个学生的总绩点为其所有课程绩点总和除以10。
Input:
输入数据中含有一些班级(≤20)。
每个班级的第一行数据n(≤10),a1,a2,a3,…,an,表示该班级共有n门课程,每门课程的学分分别为a1,a2,a3,…,an;
班级数据中的第二行数据为一个整数m(≤50),表示本班级有m个学生;
班级数据接下去有m行对应m个学生数据;
每行学生数据中的第一个为字串s1(s1中间没有空格),表示学生姓名,后面跟有n个整数s1,s2,s3,…,sn,表示该学生各门课程的成绩(0≤si≤100)。
Output:
以班级为单位输出各个学生按绩点分从大到小的排名。如果绩点分相同,则按学生名字的ASCII串值从小到大排名。
每个班级的排名输出之前应先给出一行,描述班级序号“class #:”(#表示班级序号),班级之间应空出一行。
排名时,每个学生占一行,列出名字和总绩点。
学生输出宽度为10个字符,左对齐,在空出一格后列出总绩点。
Sample Input:
1
3 3 4 3
3
张三 89 62 71
smith 98 50 80
王五 67 88 91
Sample Output:
class 1:
王五 3.26
smith 2.34
张三 2.28
Hint:
请用BCB提交
Source:
qianneng
流程:
定义学生结构体
自定义比较函数(应用运算符重载)
存学分用vector,存学生信息用set
读数据,每次循环计算绩点并存入(存入时会使用自定义排序方式)
输出
代码:
#include<iostream>
#include<string>
#include<set>
#include<vector>
#include<algorithm>
#include<iomanip>
using namespace std;
// 学生结构体
struct Student {
string s;//学生姓名
double d;//学生绩点
Student(string ss, double dd) : s(ss), d(dd) {} //构造函数
};
//比较函数,先比较绩点,再比较姓名。
bool operator<(const Student& a1, const Student& a2) {
return(a1.d != a2.d ? a1.d > a2.d : a1.s < a2.s);
}
// ma.insert(Student(s, sum / 100)); //插入时采用自定义比较规则
int main() {
int num;
cin >> num;//班级数
for (int i = 0; i < num; ++i) // 每个班级
{
int n, m;
cin >> n; // 课程数
vector<int> credit(n); // 各个课程的学分
for (int j = 0; j < n; ++j)
cin >> credit[j];
set<Student> ma; // 学生容器
cin >> m; //学生数
for (int i = 0; i < m; i++) { // 每个学生
string s;
cin >> s; // 学生姓名
double sum = 0;
for (int i = 0, a; i < n && cin >> a; ++i) // 输入一门成绩累计一次绩点
if (a >= 60)
sum += (a - 50) * credit[i];
ma.insert(Student(s, sum / 100)); //插入时采用自定义比较规则
}
cout << fixed << setprecision(2); // 保留到小数点后两位。头文件#include<iomanip>。
cout << (i ? "\n" : "") << " class " << i + 1 << endl;
set<Student>::iterator It;
for (It it = ma.begin(); it != ma.end(); ++it)
cout << left << setw(11) << it->s << it->d << endl;// 因为学生名字是左对齐 , 外加空一格 , 所以长度 11 来输出名字。头文件#include<iomanip>。
}
return 0;
}
1206 去掉双斜杠注释
题目:
去掉双斜杠注释
Time Limit:1000MS Memory Limit:32768K
Description:
将C++程序代码中的双斜杠注释去掉。
Input:
输入数据中含有一些符合C++语法的代码行。需要说明的是,为了方便编程,规定双斜杠注释内容不含有双引号。
Output:
输出不含有双斜杠注释的C++代码,原语句行格式不变,行尾也不应有空格。
Sample Input:
//======================
// simplest program
//======================
#include<iostream>
using namespace std;
//----------------------
int main(){
cout<<”hello world!\n”;
}//---------------------
Sample Output:
#include<iostream>
using namespace std;
int main(){
cout<<”hello world!\n”;
}
Source:
qianneng
知识点:
string里的:
find_first_not_of
find_last_of
find_last_not_of
string::npos
substr
流程:
每次循环输出一行,输出一行时有以下几种情况:
1 空行
2 //======================
3.1 int n = s.find ("//");
3.2 } //---------------------
判断步骤:
1 空行 // 不输出
2 非空行,打头的是双斜杠:// 不输出
3 非空行,打头的不是双斜杠,找到双引号的位置(没有双引号位置就是0):
3.1 双引号后没有双斜杠 // 原句输出
3.2 双引号后有双斜杠 // 从双斜杠起倒着找非空字符,然后将从头开始到该非空字符的字串输出
代码:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<string> s;
for (string str; getline(cin, str);)
s.push_back(str);
for (int i = 0; i < s.size(); i++) //逐行输出
{
int p = s[i].find_first_not_of(' '); // p是第一个非空字符
//1 在行中从头开始找第 1 个非空字符,如果找不到,则说明那是空行,应予以保留。
if (p == string::npos)
{
cout << endl;
continue;
}
//2 判断非空字符打头的是否双斜杠,若是,说明为整行注释,应予以去掉。
if (s[i].substr(p, 2) == "//")
{
continue;
}
//3 从行尾开始往前找双引号,若找到,就从p开始进行第4步
p = s[i].find_last_of('"');
if (p == string::npos) // 行中无双引号,就从0开始进行第4步
{
p = 0;
}
//3.1 从p开始找双斜杠,若没有找到双斜杠,则原句原样输出
p = s[i].find("//", p);
if (p == string::npos)
{
cout << s[i] << "\n";
}
//3.2 若找到双斜杠,则为了去掉双斜杠之前多余的空格,须从双斜杠起倒着找非空字符,然后将从头开始到该非空字符的字串输出
else
{
p = s[i].find_last_not_of(' ', p - 1);
cout << s[i].substr(0, p + 1) << "\n";
}
}
return 0;
}
三
1207 n!的位数
题目:
n!的位数
Time Limit:2000MS Memory Limit:65536K
Description:
针对每个非负整数n,计算其n!的位数。
Input:
输入数据中含有一些整数n(0≤n<10^7)。
Output:
根据每个整数n,输出其n!的位数,每个数占独立一行。
Sample Input:
5
6
Sample Output:
3
3
Source:
qianneng
核心思想:
n!的位数 = log1 + log2 + …… + logn (如果有小数则向上取整,应用ceil函数)
代码:
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
int n;
while (cin >> n)
{
double sum = 0;
for (int i = 1; i <= n; i++)
{
sum += log10(i * 1.0);
}
cout << ceil(sum);
}
return 0;
}
1208 排列对称串
题目:
排列对称串
Time Limit:1000MS Memory Limit:32768K
Description:
很多字串,有些是对称的,有些是不对称的,请将那些对称的字串按从小到大的顺序输出。字串先以长度论大小,如果长度相同,再以ASCII码值为大小标准。
Input:
输入数据中含有一些字串(1≤串长≤256)。
Output:
根据每个字串,输出对称的那些串,并且要求按从小到大的顺序输出。
Sample Input:
123321
123454321
123
321
sdfsdfd
121212
\\dd\\
Sample Output:
123321
\\dd\\
123454321
Source:
qianneng
代码:
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
// 判断对称
bool isSym(const string& s) {
for (int i = 0; i < s.length() / 2; ++i)
if (s[i] != s[s.length() - 1 - i]) return false;
return true;
}
// 自定义比较函数
bool myComp(const string& s1, const string& s2) { // 先按长度再按ASCII码
return s1.length() != s2.length() ? s1.length() < s2.length() : s1 <= s2;
}
int main() {
vector<string> ss;
for (string s; getline(cin, s);) {
if (isSym(s)) ss.push_back(s); // 如果是对称串就加入容器
sort(ss.begin(), ss.end(), myComp);
}
for (int i = 0; i < ss.size(); i++)
cout << ss[i] << endl;
return 0;
}
1209 勒让德多项式
题目:
勒让德多项式
Time Limit:1000MS Memory Limit:32768K
Description:
数学poly函数的展开式也称为关于x的n阶勒让德多项式,它的递推公式为:
poly_n(x)=
1 ,n=0;
x ,n=1;
( (2n-1) * x * poly_n-1(x) - (n-1) * poly_n-2(x) ) / n ,n>1
给定x,请你计算n阶勒让德多项式的值。
Input:
输入数据中含有一些浮点数x(0<x<1)。
Output:
对于每个x,分别计算2阶、3阶、4阶、5阶、6阶的勒让德多项式的值,其每个值的精度为6位小数。
输出时,先列出x的值,保留3位小数精度,然后每输出一个阶值之前,都空出2格,由此一字排开,形成一张多项式表,见样本输出格式.
其中标题行上第一个x对准小数点后第一位,后面的每个字母p对准下列的小数点位置。
Sample Input:
0.2 0.3 0.35
Sample Output:
x p2(x) p3(x) p4(x) p5(x) p6(x)
0.200 -0.440000 -0.280000 0.232000 0.307520 -0.080576
0.300 -0.365000 -0.382500 0.072938 0.345386 0.129181
0.350 -0.316250 -0.417812 -0.018723 0.322455 0.222511
Hint:
请用BCB编译器提交
Source:
qianneng
流程:
每行输出格式:
2个空格 -> x值(保留到小数点后3位) -> p2(x)的值 -> p3(x)的值 -> p4(x)的值 -> p5(x)的值 -> p6(x)的值 (p_n(x)保留6位小数,右对齐占11位,不足的地方用空格补齐)
代码:
#include <iostream>
#include<iomanip>
using namespace std;
int main()
{
int i = 0;
cout << fixed;
for (double x; cin >> x;) {
if (i == 0) {
cout << " x p2(x) p3(x) p4(x) p5(x) p6(x)" << endl;
i = 1;
}
cout << setprecision(3) << x ;
double as = 1; // poly_n-1(x)
double at = x; // poly_n-2(x)
for (int n = 2; n <= 6; n++) {
double au = ((2 * n - 1) * x * at - (n - 1) * as) / n;
as = at;
at = au;
cout << setprecision(6) << setw(11) << au;
}
cout << endl;
}
return 0;
}
1210 立方数与连续奇数和
题目:
立方数与连续奇数和
Time Limit:5000MS Memory Limit:32768K
Description:
一个整数的立方数,可以表示为整数项连续奇数的和,例如:
3^3 = 7+9+11, 4^3 = 13+15+17+19.
针对每个正整数n,输出表示其立方数的连续奇数和。
Input:
输入数据中含有一些整数n(1≤n≤100)。
Output:
根据每个整数n,输出其值等于n^3的n项连续奇数和,格式见样本输出,每个表达式输出完成后应回车。
Sample Input:
3 4 8
Sample Output:
3^3=7+9+11
4^3=13+15+17+19
8^3=57+59+61+63+65+67+69+71
Source:
qianneng
核心思想:
连续奇数的首项 begin 为 :
begin = 2*m + 1 = n * n - n +1 = x * x - x + 1
末项为 :
begin +2*x -1
步长为 2.
代码:
#include <iostream>
using namespace std;
int main()
{
for (int x; cin >> x;) {
int begin = x * x - x + 1;
cout << x << "^3=" << begin;
for (int i = begin + 2; i <= begin + 2 * x - 1; i += 2)
cout << "+" << i;
cout << endl;
}
return 0;
}
1211 菲波那契数
题目:
菲波那契数
Time Limit:1000MS Memory Limit:32768K
Description:
已知菲波那契数的定义:
f(0) = 0
f(1) = 1
f(n) = f(n-1) + f(n-2) n>1,整数
根据输入数据中的n,输出第n项菲波那契数。
Input:
输入数据中含有一些整数n(0≤n≤46)。
Output:
根据每个整数n,输出其第n项菲波那契数,每个数占独立一行。
Sample Input:
5
6
7
8
9
40
Sample Output:
5
8
13
21
34
102334155
Source:
qianneng
代码:
// 高效方法
// 一
#include<iostream>
using namespace std;
int main() {
for(int n;cin>>n;) {
int a=0,b=1,c;
for(int i=2;i<=n;i++)
c=a+b,a=b,b=c;
cout<<c<<"\n";
}
}
// 二
#include<iostream>
using namespace std;
int main() {
int a[47] = { 0,1 };
for (int i = 2; i < 47; ++i) // 预存实验结果
a[i] = a[i - 1] + a[i - 2];
for (int n; cin >> n;)
cout << a[n] << "\n";
}
// 递归方法
#include<iostream>
using namespace std;
int fob(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
else return fob(n - 1) + fob(n - 2);
}
int main() {
for (int n; cin >> n;)
cout << fob(n) << "\n";
}
四
1212 简单四则运算
题目:
简单四则运算
Time Limit:1000MS Memory Limit:32768K
Description:
给定一些没有括号的四则运算表达式,求其结果。
Input:
输入数据中含有一些表达式(数量≤1000,长度按含有的运算计,运算符≤30),表达式的运算符只含有加、减、乘、除。表达式中每个数的精度范围在double型内,表达式中没有任何其他运算符,没有括号。
Output:
对每个表达式,计算其结果。按科学计数法输出,精度按6位小数,每个结果应占独立一行。如果表达式发生除0的情况,则对该表达式直接输出“DivByZero”。
Sample Input:
3+5.0
6-2*7
6-2/0
3+5*6+1
3+5+1*7
1+2-3*4+5/6-4*3*2*1*1+2+3+4+5
Sample Output:
8.000000e+00
-8.000000e+00
DivByZero
3.400000e+01
1.500000e+01
-1.816667e+01
Hint:
请用BCB编译器提交
Source:
qianneng
核心思想:
将循环每次处理的状态看作是 a + b ☆ c,根据每次读入的 ☆ 进行相应操作。
流程:
可以将循环每次处理的状态看作是 , 已有 a 、 + 、 b , 而观察将要读入的☆和 c , 即每次
a + b ☆ c // 不忙做 + 操作
于是 , a 的初值应为 0 , 表达式最先读入的是 b , 然后不断处理☆和 c . 其中☆可能是加、减、乘、除操作符 , 对于☆和 c 将做如下处理:
☆为 + : 则先做前面的 + , 即 a = a + b , b = c ; 继续新一轮读入☆ , 和 c 循环 .
☆为 - : 则先做前面的 + , " - " 相当于 + 负数 , 即 a = a + b , b = - c ; 继续新一轮读入☆ , 和 c 循环 .
☆为 × : 则先做后面的 * , 即 b = b * c ; 继续新一轮读入☆ , 和 c 循环 .
☆为 / : 当 c ! = 0 时 , 则先做后面的 / , 即做 b = b / c ; 继续新一轮读入☆ , 和 c 循环 , 而
当 c = 0 时 , 则直接输出 DivByZero 并中断本次表达式计算循环 .
代码:
#include<iostream>
#include<string>
#include<sstream>
using namespace std;
int main() {
for (string s; getline(cin, s) && s != "";)
{
istringstream sin(s);
char op;
double a = 0, b, c;
for (sin >> b; sin >> op >> c;)
{
switch (op)
{
case'-':c = -c;
case'+':a += b; b = c; break;
case'*':b *= c; break;
case'/':
if (c == 0) {
cout << "DivByZero" << endl;
goto A;
}
else b /= c;
}
}
cout << scientific << a + b << endl;
A:;
}
return 0;
}
1213 大数加
题目:
大数加
Time Limit:1000MS Memory Limit:32768K
Description:
给定一些大数,请计算其和。
Input:
输入数据中含有一些数据组(数量≤100),每组数据由一个整数n(n≤100)领衔,后跟n个大整数(0≤大数,大数位数≤200),若n=0则表示输入结束。
Output:
输出每组数据所计算的大数和,每个结果单独占一行。
Sample Input:
5
123456789
6287999980
5645645654654
5
79
0
Sample Output:
5652057111507
Source:
qianneng
核心思想:
1. 用字符串string表示大数。因为大数200位长,无法用计算机内部数据类型表示。
2. 大数求和的方法:
(1)因为输入的数字串是从高位到低位排列的,两个大数只有高位是对齐的,低位并没有对齐。知道了这个原则,就可以将两个大数都逆转一下,然后进行反向相加处理。例如 , 对 123+56789 进行倒序相加 :
3 2 1 0 0 0 0
+ 9 8 7 6 5 0 0
———————————————————
2 1 9 6 5 0 0 //将此结果翻转得“0056912”,去掉前面的0,即是结果
(2)如何进行相加呢?计算每位的和,本位是对于10的余数,进位是商。
流程:
每次循环,调用add函数,将新来的大数s加在已有的大数a上
add():
a的每一位a[ai]与s的每一位s[si]相加,再加上进位,得到的和,除以10取余保留在本位,除以10取整进到更高位。
(在下面代码的add里:因为a本来就是反转过来的,低位在前,所以从0开始。没有把参数 s 的顺序颠倒过来,而是一边从左加起,一边却从右加起,因为当 s 被逐位加完后,还要考虑到此时是否仍有进位,以便再继续加下去。)
/*
注意:
考虑到一定不能让运算溢出 , 被加数的位数应该取最大大数的位数 , 再加上可能进行的操作数量的对数的上限。例如 , 最大位数为 200 位的大数 , 要做 10000 多次运算 , 则可能会达到 205 位数 .
*/
代码:
#include <iostream>
using namespace std;
void add(string &a,const string &s) // 反向相加
{
int temp=0; // 进位
for(int ai=0,si=s.length()-1; si>=0 || temp; ++ai,--si)
{
temp+=a[ai]-'0'+(si>=0?s[si]-'0':0);
a[ai]=temp%10+'0'; // 本位
temp=temp/10; // 进位
}
}
int main()
{
const int bitnum=205;
for(int num;cin>>num && num;)
{
string a(bitnum,'0');
for(string s;num--&&cin>>s;)
add(a,s); // 反向相加
reverse(a.begin(),a.end()); // 结果翻转
cout<<a.substr(a.find_first_not_of('0'))<<"\n";
}
return 0;
}
1214 大数和
题目:
大数和
Time Limit:1000MS Memory Limit:32768K
Description:
给定一些大数,请计算其和。
Input:
输入数据中含有一些数据组(数量≤100),每组数据由一个整数n(n≤100)领衔,后跟n个大数(大数位数≤200),若n=0则表示输入结束。
Output:
输出每组数据所计算的大数和,每个结果单独占一行。
Sample Input:
2
123123123123123123123123123123123
-2
2
43242342342342
-1234567654321
0
Sample Output:
123123123123123123123123123123121
42007774688021
Source:
qianneng
核心思想:
本题与上题的差异在于本题允许负数。思路:将负数像二进制补码的方式那样取补,再做加法。
流程:
main():
1. 读入大数:
如果发现带有 " - " 号 , 先删除之。另外,由于补码的加法操作会涉及高位的 9 , 与最大位数很有关系 , 所以每个正数负数都应扩充到最大位。
然后取补comple()。
2. 正负数准备好后进行加的操作。add()
3. 最后打印结果:
先判断其正负性确定是否输出“-”,a[0]为9就说明最后结果是负数的补,因为大数位数不超过200,这里设置的bitnum = 205,最高位是9,那么肯定是负数的补码。
然后去掉前导 0 ,打印输出.
另外,还要考虑到如果一个大数等于 0 , 则去 0 操作将导致打印一个空串 , 这时应特别地打印一个 0 .
add():
a的每一位a[ai]与s的每一位s[si]相加,再加上进位 -> 得到的和 -> 除以10取余保留在本位,除以10取整进位到更高位。
comple():
每一位都用 9 去减;
然后个位数再加 1,( 在最低位而不是在最高位 )。如果该位为 9 说明加 1 之后有进位,继续循环把它再高一位加 1,如果不是 9 说明加 1 之后不会进位,退出循环,加1操作结束。
/*
取补例如,最大为 5 位数的 -123 取补后得到 :
99877
在打印的时候,如果发现最高位是 9,则应先取补,取补之后负号相反。上面的数取补后又变回原来的值 :
-00123
再去掉前面 0 就是正确的结果了。
这种方法的缺点是先要对负数取补,然后再做加法。
*/
/*
另一种思路:把大数的第 1 位拿来作符号位,通过第 1 位的情况来决定到底是做加法还是做减法。
这种思路的缺点是 ,每次做减法时,先要判断两个数的大小,然后较大数的符号决定运算结果的符号。若较小数减去较大数,则会导致溢出。
*/
代码:
#include <iostream>
using namespace std;
void comple(string& s) // 对一个十进制大数取补
{
for (int i = 0; i < s.length(); ++i)
s[i] = '9' - s[i] + '0'; // 每一位都用 9 去减
for (int i = s.length() - 1; i >= 0; i--) // 最低位加 1
if (s[i] == '9') s[i] = '0';
else { s[i]++; break;}
}
void add(string& a, const string& s)
{
int temp = 0;
for (int ai = 0, si = s.length() - 1; si >= 0 || temp > 0; ++ai, --si)
{
temp += a[ai] - '0' + (si >= 0 ? s[si] - '0' : 0);
a[ai] = temp % 10 + '0';
temp = temp / 10;
}
}
int main()
{
const int bitnum = 205;
for (int num; cin >> num && num;)
{
string a(bitnum, '0'); // 结果
for (string s; num-- && cin >> s;)
{
if (s[0] == '-') // 如果是负数
{
s = s.substr(1);
s = string(bitnum - s.length(), '0') + s;
comple(s);
}
else // 如果是正数
s = string(bitnum - s.length(), '0') + s;
add(a, s); // 相加
}
// 打印结果
reverse(a.begin(), a.end());
if (a[0] == '9')
{
comple(a);
cout << "-";
}
int pos = a.find_first_not_of('0');
if (pos == string::npos)
cout << "0\n";
else
cout << a.substr(pos) << "\n";
}
return 0;
}
1215 彼此两点最近
题目:
彼此两点最近
Time Limit:1000MS Memory Limit:32768K
Description:
给定一些平面上的点,求出彼此距离最近的两点。
Input:
输入数据中含有一些数据组(数量≤100),每组数据由一个整数n(2≤n≤102)领衔,后跟n个平面坐标点x、y(-10000≤整数x,y≤10000)。若n=0则表示输入结束。
Output:
每组数据都有彼此距离最短的坐标点,输出所有彼此距离最短的两点x和y坐标,坐标应以x的大小和y的大小依次输出,用括号将其括起来,坐标点之间空一格,若有多对最短距离点,则换行输出,坐标点小的在前,大的在后。每组数据的结果之间空一行。
Sample Input:
4
1 2
0 0
3 6
7 2
3
1 3
3 1
0 0
11
1 2
2 3
3 5
7 5
9 6
9 7
10 8
1 9
9 1
10 11
10 12
0
Sample Output:
(0,0) (1,2)
(1,3) (3,1)
(9,6) (9,7)
(10,11) (10,12)
Source:
qianneng
核心思想:
1. 点的设计:
struct Point{
int a,b;
Point(int x=0,int y=0):a(x),b(y){ }
//构造函数,与结构体名字必须完全相同,用于对结构体类型变量初始化
}
构造函数初始化示例:
Point p1;
p1=Point(3,5);
2. 两点之间距离计算表示方式:
int dis(const Point& p1, const Point& p2){
return(p1.a-p2.a)*(p1.a-p2.a)+(p1.b-p2.b)*(p1.b-p2.b);
// 因为并不要求两点之间的精确距离,而只要求比较距离的长短,因此计算距离没有必要做开平方操作。
}
3. sort自定义排序方式
bool operator<(const Point& a, const Point& b) {
return a.x != b.x ? a.x < b.x : a.y < b.y;
}
流程:
main():
遍历每一对点计算其距离,在比较中,
如果两点的距离比原来的最短距离更短,则更新最短距离,将该两点的资料放入结果中;
如果两点的距离等于原来最短的距离,则将该两点增加到结果容器中,
如果两点距离大于原来最短距离,则放弃之。
最后输出:
注意,结果r中的数据的意义并不是坐标点,而是最短距离的两个点在坐标点容器 p 中的序号。r[i].x是i,r[i].y是j。要输出p[i].x,p[i].y,和p[j].x,p[j].y。
代码:
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct Point {
Point(int dx = 0, int dy = 0) : x(dx), y(dy) { }
int x, y;
};
bool operator<(const Point& a, const Point& b) { // 自定义排序方式
return a.x != b.x ? a.x < b.x : a.y < b.y;
}
int dis(const Point& a, const Point& b) {
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
int main() {
for (int N; cin >> N && N; ) {
vector<Point> p(N), r; // 数组p存放点,r存放结果(两个点在容器 p 中的序号)
for (int i = 0; i < N; ++i)
cin >> p[i].x >> p[i].y;
sort(p.begin(), p.end()); // 按自定义排序方式排序,让点呈大小排列
int m = 1 << 30;
for (int i = 0; i < N - 1; ++i) // 遍历每一对点p[i], p[j]
for (int j = i + 1; j < N; ++j) {
int k = dis(p[i], p[j]);
if (k == m)
r.push_back(Point(i, j));
else if (k < m) {
r.clear();
r.push_back(Point(i, j));
m = k;
}
}
for (int i = 0; i < r.size(); ++i)
cout << "(" << p[r[i].x].x << "," << p[r[i].x].y << ") "
<< "(" << p[r[i].y].x << "," << p[r[i].y].y << ")\n";
}
return 0;
}
1216 离直线最近的点
题目:
离直线最近的点
Time Limit:1000MS Memory Limit:32768K
Description:
给定一根直线和一些平面上的点,求出到直线距离最近的点。
Input:
输入数据中含有一些数据组(数量≤100),每一组数据的第一行为一个整数n(1≤n≤100),表示本组数据中将有n个坐标点,若n为0,表示输入结束。第二行为四个整数(依次为x1,y1,x2,y2),表示确定一根直线的两个坐标点,紧接着有n对整数x,y(-10000≤x,y≤10000),表示n个坐标点。
Output:
每组数据中都有距直线最短的坐标点,输出其x和y坐标,若满足条件的点不止一个,则换行继续输出,每组数据之间应空一行。
Sample Input:
4
1 2 3 4
3 8
10 10
7 2
900 1
0
Sample Output:
10 10
Source:
qianneng
流程:
main():
输入直线,每输入一个点,计算其到直线的距离,在比较中,
如果比原来的最短距离更短,则更新最短距离,将该点的资料放入结果中;
如果等于原来最短的距离,则将该点增加到结果容器中,
如果大于原来最短距离,则放弃之。
最后输出所有点,保证输出从小到大
代码:
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct Point
{
Point(int dx = 0, int dy = 0) : x(dx), y(dy) { }
int x, y;
};
bool operator<(const Point& a, const Point& b)
{
return a.x != b.x ? a.x < b.x : a.y < b.y;
}
int main()
{
for (int n, w = 0; cin >> n && n;)
{
int x1, y1, x2, y2, m = 1 << 30; // 距离初值故意设得很大
cin >> x1 >> y1 >> x2 >> y2;
int A = y2 - y1, B = x1 - x2, C = y1 * x2 - x1 * y2;
vector<Point> r; // 结果向量
for (int i = 1, x, y, k; i <= n && cin >> x >> y; ++i)
{
if ((k = x * A + y * B + C) < 0)
k = -k; // 取整型绝对值
if (k == m)
r.push_back(Point(x, y));
else if (k < m) {
r.clear();
r.push_back(Point(x, y));
m = k;
}
}
sort(r.begin(), r.end()); // 保证输出从小到大 , 注意使用默认比较函数
cout << (w++ ? " \n " : "");
for (int i = 0; i < r.size(); ++i)
cout << r[i].x << " " << r[i].y << " \n ";
}
return 0;
}
五
1217 大数乘
题目:
大数乘
Time Limit:5000MS Memory Limit:32768K
Description:
给定一些大数,请计算其积。
Input:
输入数据中含有一些整数对(对数≤1000),若某对整数(整数位数≤200)的值为0 0,则表示输入结束。
Output:
每对整数对应一个乘法计算结果,输出该结果,每个结果输出完后应回车。
Sample Input:
2 3
12 34
0 0
Sample Output:
6
408
Source:
qianneng
知识点:
1. 大数乘法方法
2. string
string(int n, char c); //使用n个字符c初始化
string substr(int pos = 0, int n = npos) const; //返回由pos开始的n个字符组成的字符串;只有一个数字pos,表示从该数字到结尾。
size_t find_first_not_of ( char c, size_t pos = 0 ); // 在字符串中查找一个字符,并返回该内容在字符串中第一次出现的位置。
3. reverse函数
功能:反转在[first,last)范围内的顺序(包括first指向的元素,不包括last指向的元素),reverse函数无返回值。多用于字符串、数组、容器。
头文件是#include<algorithm>
核心思想:
multi():
1. 变量存储:因为大数可能大到 200 位,可以将大数值放在 string 变量中,以两个 string 变量为参数来调用大数乘法函数,返回乘法的结果也是 string 变量。
2. 符号处理:在做乘法运算时先将负号去掉。结果的正负性可以通过判断两个数的正负性来解决。
3. 乘法运算:为了方便乘法运算,可以先将两个大数的顺序都倒过来,然后都从下标 0 开始进行逐位相乘,做完乘法后,再倒回去。
流程:
1. 处理符号:去掉两个大数的负号,计算结果的正负。
2. 将结果串取到最大可能的长度,即两个大数的长度和。
3. 做乘法运算:两重循环,它先按乘数的位进行循环,然后再按被乘数的位进行循环,逐位相乘,注意保留进位。相乘与赋值时,要留意字符与数字的转换。
4. 逆转顺序,若前面有多余的 0,则再去掉前面多余的 0。
/*
注意:
该代码的局限性在于,大数的长度受到 string 长度的限制,所以最多能做千位级的运算 , 若上万位的大数 , 就要用其他容器了。
*/
代码:
#include <iostream>
using namespace std;
string multi(const string &a,const string &b)
{
if(a=="0"||b=="0")
return "0";
string aa(a[0]=='-'? a.substr(1):a);
string bb(b[0]=='-'? b.substr(1):b);
string sign=((a[0]=='-')+(b[0]=='-')==1?"-":"");
string s(aa.length() + bb.length(),'0');
reverse(aa.begin(),aa.end());
reverse(bb.begin(),bb.end());
// 乘法运算!乘法运算!乘法运算!
for(int j=0;j<bb.length();++j) // 先按乘数的位进行循环
{
if(bb[j]=='0')
continue;
int temp=0;
for(int i=0;i<aa.length();++i) // 再按被乘数的位进行循环
{
temp=temp+(aa[i]-'0')*(bb[j]-'0')+(s[j+i]-'0'); // 逐位相乘
s[j+i]=temp%10+'0'; // 保留进位
temp=temp/10;
}
s[aa.length()+j]+=temp;
}
reverse(s.begin(),s.end());
return sign+s.substr(s.find_first_not_of('0'));
}
int main()
{
for(string a,b;cin>>a>>b&&(a!="0"||b!="0");)
cout<<multi(a,b)<<"\n";
return 0;
}
1218 n!中的0
题目:
n!中的0
Time Limit:1000MS Memory Limit:32768K
Description:
贝贝很想搞清n!中到底有几个0,但是计算n!已经很不容易,很多很多位啊,算得头都晕了,再开始数0的个数,怎么也数不清了,其实这件事情让计算机做,应该是比较容易做的,请你帮助贝贝解决这个难题吧。
Input:
输入数据中包含一些整数,每个整数不大于1000。
Output:
输出每个整数的阶乘中0的个数,每个结果都回车。
Sample Input:
3
8
9
10
Sample Output:
0
2
1
2
Source:
qianneng
核心思想:
1. 大数1000!有2568位,无法用计算机内部数据类型直接表示。方法是用数组一位一位存放,用数组 a 存放阶乘值,数组zeros存放 每个n!的0的个数(下标是n)。
2. 采用迭代:已知(n-1)!,乘以n得到n!
3. 乘法方法
代码流程:
proc()函数构建zeros表:
for(n 从 2 到 1000):
先计算尾部 0 的个数 beg , 因为 0 值可以免于计算 ;
再计算 n ! 的位数 e , 它是在已知 ( n -1 ) ! 的位数前提下进行计算的 ; (为什么是double?)
然后做 n ! 的计算;
最后调用 count 算法计算 n ! 中的 0 的个数 ,
【等到下次循环,数组a存储的 n ! 的值就被 ( n +1 ) ! 所代替而不复存在了,因此在循环中及时计算求值是要点。】
输入数字n,输出zeros[n],结束。
/*
注意:
1. a数组保存着的数是反着放的。
2. 做 n! 的计算那里,就相当于平时的整数乘法,被乘数是n,乘数是a。
乘数末尾的一串0可免于参加运算,被乘数n与乘数的每一位数a[j]相乘,本位=(低位的进位+现在的乘积)%10,进位到an,an /= 10变为更高一位的进位。???
*/
源代码:
#include <iostream>
#include<cmath>
using namespace std;
const int Factor = 1000;
int zeros[Factor], a[2568] = { 1 }; // 1000!有2568位
void proc() {
double bitNum = 1.0;
for (int n = 2, beg = 0, e = 0; n <= Factor; ++n) {
while (a[beg] == 0) {
beg++; // 累计尾部0的个数
}
e = bitNum += log10(n * 1.0); // n!的位数
for (int j = beg, an = 0; j < e; j++, an /= 10) { // 做 n! 的计算
a[j] = (an += n * a[j]) % 10;
}
zeros[n] = count(a + beg, a + e, 0) + beg; // 计算 n ! 中的 0 的个数
}
}
int main() {
proc();
int n;
while (cin >> n) {
cout << zeros[n] << endl;
}
return 0;
}
1219 整数模
题目:
整数模
Time Limit:1000MS Memory Limit:32768K
Description:
a除以m的余数称为a对于m的模。求a^p对于m的模。
Input:
输入数据中含有一些数据组,每个数据组占一行,包括a、p、m(0<a,p<2^32,1≤m<2^16)三个整数,若三个数都为0,则表示输入结束。
Output:
针对每组数据,输出a的p次幂对于m的模,每个结果占一行。
Sample Input:
3 18132 17
0 0 0
Sample Output:
13
Source:
qianneng
核心思想:
利用数学定理:
1. (a*b)%m=[(a%m)*(b%m)]% m
当p为奇数时,a^p%m = [(a^(p-1)%m) * (a%m)]%m
当p为偶数时,a^p%m = [ (a^(p/2)%m) * (a^(p/2)%m) ]%m
2. a^p %m= [(a%m)^p]%m
代码流程:
module函数用来求模:
如果P=0,直接返回1;
如果p是奇数,返回[module(a,p-1,m) * (a%m)] % m
如果p是偶数,返回[module(a,p/2,m) * module(a,p/2,m)] % m
输入a、p、m,用module(a % m, p, m)求模并输出。
源代码:
#include <iostream>
using namespace std;
int module(unsigned a, unsigned p, int m) {
if (p == 0) return 1;
if (p % 2) return a % m * module(a, p - 1, m) % m;
a = module(a, p / 2, m);
return a * a % m;
}
int main() {
unsigned a, p, m;
while (cin >> a >> p >> m && a != 0 && p != 0 && m != 0) {
cout << module(a % m, p, m) << endl;
}
return 0;
}
1220 N个胜利者
题目:
N个胜利者
Time Limit:2000MS Memory Limit:32768K
Description:
n个小孩围成一圈做游戏,游戏将决出若干个胜利者。 假定一个数m,从第1个小孩起,顺时针数数,每数到第m个小孩时,该小孩离开。接着又从下一个小孩开始数数,数到第m个小孩时,该小孩也离开,如此不断反复进行,最后剩下的k个小孩便是胜利者。对于一定的n、m、k,究竟胜利者是哪些呢?
Input:
输入数据有一些数据组,每组数据含有整数n、m、k(1≤ n, m, k≤50)),分别表示小孩数,游戏中每次数数的个数和最后剩下的k个胜利者。
Output:
对于每组数据,按从小到大的顺序输出一列获胜小孩的位置。每组获胜序列之间应回车。
Sample Input:
10 3 3
Sample Output:
4 5 10
Source:
qianneng
核心思想:
1. 一直数m个然后删除,重复操作,直到剩下w个
2. 这里的环链表用数组存,数组里每个元素是个结构体,数组最后一个元素指向第一个元素;
3. 链表的删除操作(要用两个指针)
流程:
首先定义一个小孩结构体Boy,里面有小孩的编号和指向下一个小孩的指针。
1. 输入小孩数、计数间隔、获胜者数量;
2. 创建环链表:
按小孩数new一个Boy类型的数组。再进行初始化,给每个小孩编号,让每个小孩的指针指向下一个小孩。
【这里做环结构的时候巧用%,让第i-1个孩子指向第i个孩子很好做,但是如何让最后一个孩子(下标n-1)指向第1个孩子(下标0)呢?就是当i=n的时候,利用i%n=0,当然前面i不大于n的时候,i%n依然是i】;
3. Boy* pivot指向前一个小孩,Boy* pCurrent指向当前小孩,从链表开始位置第0个开始,也就是pCurrent先指向第0个;
4. 循环执行以下步骤,直到达到获胜者数量:
数m个小孩:pCurrent 和 pivot向前移 m 个(也就是pivot = pCurrent,pCurrent = pCurrent->next);
小孩离队:删除pCurrent(也就是pivot->next = pCurrent->next,pCurrent = pivot);
5. 输出获胜者:
vector数组wins存小孩的编号;
用一个指针p遍历,从pCurrent开始,p逐渐往后移,直到又遇到pCurrent为止;
sort(wins.begin(), wins.end());
遍历输出wins;
代码:
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct Boy {
int code; // 小孩编号
Boy* next; // 指向下一个小孩
};
class BoyRing {
Boy* pBegin, * pivot, * pCurrent; // 小孩结构指针,前驱指针,当前小孩指针
public:
BoyRing(int n); // 构造函数
void countBy(int m); // 数m个小孩
int getNum() const; // 返回当前小孩编号
void disengage(); // 小孩离队
void printALL()const; // 输出所有小孩
~BoyRing(); // 析构函数
};
class Jose {
int n, m, s, w; // 小孩数,计数间隔,开始位置,获胜者
public:
Jose(int boys, int interval, int begin = 1, int wins = 1); //构造函数
void getWinner() const; //求获胜者
};
int main() {
int n, m, k;
while (cin >> n >> m >> k) { // 小孩数,计数间隔,获胜者
Jose(n, m, 1, k).getWinner();
}
return 0;
}
// 构造函数
Jose::Jose(int boys, int interval, int begin, int wins)
:n(boys), m(interval), s(begin), w(wins) {
// 小孩校验,开始位置校验,计数间隔校验
}
// 求获胜者
void Jose::getWinner()const {
BoyRing x(n); //创建环链表
x.countBy(s - 1); //转到链表开始位置
for (int i = 1, numinLine = 0; i < n - w + 1; ++i) { // 循环执行以下步骤,直到达到获胜者数量
x.countBy(m); //数m个小孩
x.disengage(); //小孩离队
}
x.printALL(); //输出获胜者
}
// 构造函数
BoyRing::BoyRing(int n) {
if (n < 2) throw exception();
pBegin = new Boy[n]; // 按小孩数申请空间
for (int i = 1; i <= n; i++) { // 初始化为环结构,小孩编号
pBegin[i - 1].code = i;
pBegin[i - 1].next = &pBegin[i % n];
}
pivot = pCurrent = &pBegin[n - 1]; // 指针赋初值
}
// 数m个小孩
void BoyRing::countBy(int m) {
for (int i = 1; i <= m; ++i) {
pivot = pCurrent;
pCurrent = pCurrent->next;
}
}
// 返回当前小孩编号
int BoyRing::getNum() const {
return pCurrent->code;
}
// 小孩离队
void BoyRing::disengage() {
pivot->next = pCurrent->next;
pCurrent = pivot;
}
// 输出所有小孩
void BoyRing::printALL()const {
vector<int> wins;
int numinLine = 0;
Boy* p = pCurrent;
while(1) {
wins.push_back(p->code);
p = p->next;
if (p == pCurrent) {
break;
}
}
sort(wins.begin(), wins.end());
for (int win : wins) {
cout << win<<" ";
}
cout << endl;
}
// 析构函数
BoyRing::~BoyRing() {
delete[] pBegin; // 释放空间
}
1221 表达式个数
题目:
Time Limit:1000MS Memory Limit:32768K
Description:
1到N的序列: 1 2 3 4 5 ...N 每两个数之间插入 + 或 -,求其和恰为M的不同表达式个数。例如:N为7,M为0,则有: 1 + 2 - 3 + 4 - 5 - 6 + 7 = 0 1 + 2 - 3 - 4 + 5 + 6 - 7 = 0 1 - 2 + 3 + 4 - 5 + 6 - 7 = 0 1 - 2 - 3 - 4 - 5 + 6 + 7 = 0 所以N,M分别为7和0时,共有4种不同表达式。
Input:
输入中有若干行数据。每行中包含两个整数N(1≤ N ≤ 13),M(0≤ M ≤ N(N+1)/2)。M表示在1到N之间的各个间隙,使用 + 或 - 操作符,以构成表达式所计算的值。
Output:
对每个N和M,输出能够构成的表达式个数。如果没有,则应输出“NO”,每个结果占一行。
Sample Input:
7 0
3 2
2 1
Sample Output:
4
1
NO
Source:
qianneng
核心思想:
1. 用map存N、M、和对应的表达式个数num,后续再查表得结果。
2. 对于一个N值,怎么计算出其所有种表达式的值呢?方法是用二进制数来表示符号序列。
/*
如当N=5:
i 表示符号序列,是二进制的0000~1111,每一位代表一个符号,1是+,0是-:
sum 刚开始是 1;
j 是下一个要加的数,从 2 到 N,j 也用来计算二进制 i 右移的位数:
sum + = (判断符号位)j;
j 循环结束就求得某一个表达式的sum值了
以N=5的时候,1+2+3+4-5为例:
某趟循环i=1110:
j从2到5:
当j=2时,把i右移5-2=3位,得1(110),然后最低位与1相与,判断它是否是1,是1,那么sum+= j,否则sum+= -j; */
流程:
// 预存结果
构建表,map里的每个元素存储3个变量:N、M、表达式个数num。N*100+M放在first,表达式个数放在second,每个N和M都有一个唯一的对应值。
遍历每个N:
遍历每个M:
遍历每种符号序列对应的表达式的结果:
用一个循环计算表达式结果sum;
若sum等于M,表达式个数num++;
把N、M、num存入map中;
// 用户输入
用 find 找 N*100+M 的对应值;
输出,程序结束。
代码:
#include <iostream>
#include<map>
#include<algorithm>
using namespace std;
int main() {
map<int, int> ma;
for (int N = 1; N <= 13; ++N) {
for (int M = 0; M <= N * (N + 1) / 2; ++M) {
int num = 0;
for (int i = 0; i < 1 << N - 1; ++i) {
int sum = 1;
for (int j = 2; j <= N; ++j) {
sum += (i >> N - j & 1 ? -j : j);
}
num += (sum == M);
}
if (num) {
ma[N * 100 + M] = num;
}
}
}
for (int N, M; cin >> N >> M;) {
if (ma.find(N * 100 + M) != ma.end()) {
cout << ma[N * 100 + M] << endl;
}
else {
cout << "No\n";
}
}
}
for (int i = 1, numinLine = 0; i < n - w + 1; ++i) { // 循环执行以下步骤,直到达到获胜者数量
x.countBy(m); //数m个小孩
x.disengage(); //小孩离队
}
x.printALL(); //输出获胜者
}
// 构造函数
BoyRing::BoyRing(int n) {
if (n < 2) throw exception();
pBegin = new Boy[n]; // 按小孩数申请空间
for (int i = 1; i <= n; i++) { // 初始化为环结构,小孩编号
pBegin[i - 1].code = i;
pBegin[i - 1].next = &pBegin[i % n];
}
pivot = pCurrent = &pBegin[n - 1]; // 指针赋初值
}
// 数m个小孩
void BoyRing::countBy(int m) {
for (int i = 1; i <= m; ++i) {
pivot = pCurrent;
pCurrent = pCurrent->next;
}
}
// 返回当前小孩编号
int BoyRing::getNum() const {
return pCurrent->code;
}
// 小孩离队
void BoyRing::disengage() {
pivot->next = pCurrent->next;
pCurrent = pivot;
}
// 输出所有小孩
void BoyRing::printALL()const {
vector<int> wins;
int numinLine = 0;
Boy* p = pCurrent;
while(1) {
wins.push_back(p->code);
p = p->next;
if (p == pCurrent) {
break;
}
}
sort(wins.begin(), wins.end());
for (int win : wins) {
cout << win<<" ";
}
cout << endl;
}
// 析构函数
BoyRing::~BoyRing() {
delete[] pBegin; // 释放空间
}
1221 表达式个数
题目:
Time Limit:1000MS Memory Limit:32768K
Description:
1到N的序列: 1 2 3 4 5 ...N 每两个数之间插入 + 或 -,求其和恰为M的不同表达式个数。例如:N为7,M为0,则有: 1 + 2 - 3 + 4 - 5 - 6 + 7 = 0 1 + 2 - 3 - 4 + 5 + 6 - 7 = 0 1 - 2 + 3 + 4 - 5 + 6 - 7 = 0 1 - 2 - 3 - 4 - 5 + 6 + 7 = 0 所以N,M分别为7和0时,共有4种不同表达式。
Input:
输入中有若干行数据。每行中包含两个整数N(1≤ N ≤ 13),M(0≤ M ≤ N(N+1)/2)。M表示在1到N之间的各个间隙,使用 + 或 - 操作符,以构成表达式所计算的值。
Output:
对每个N和M,输出能够构成的表达式个数。如果没有,则应输出“NO”,每个结果占一行。
Sample Input:
7 0
3 2
2 1
Sample Output:
4
1
NO
Source:
qianneng
核心思想:
1. 用map存N、M、和对应的表达式个数num,后续再查表得结果。
2. 对于一个N值,怎么计算出其所有种表达式的值呢?方法是用二进制数来表示符号序列。
/*
如当N=5:
i 表示符号序列,是二进制的0000~1111,每一位代表一个符号,1是+,0是-:
sum 刚开始是 1;
j 是下一个要加的数,从 2 到 N,j 也用来计算二进制 i 右移的位数:
sum + = (判断符号位)j;
j 循环结束就求得某一个表达式的sum值了
以N=5的时候,1+2+3+4-5为例:
某趟循环i=1110:
j从2到5:
当j=2时,把i右移5-2=3位,得1(110),然后最低位与1相与,判断它是否是1,是1,那么sum+= j,否则sum+= -j; */
流程:
// 预存结果
构建表,map里的每个元素存储3个变量:N、M、表达式个数num。N*100+M放在first,表达式个数放在second,每个N和M都有一个唯一的对应值。
遍历每个N:
遍历每个M:
遍历每种符号序列对应的表达式的结果:
用一个循环计算表达式结果sum;
若sum等于M,表达式个数num++;
把N、M、num存入map中;
// 用户输入
用 find 找 N*100+M 的对应值;
输出,程序结束。
代码:
#include <iostream>
#include<map>
#include<algorithm>
using namespace std;
int main() {
map<int, int> ma;
for (int N = 1; N <= 13; ++N) {
for (int M = 0; M <= N * (N + 1) / 2; ++M) {
int num = 0;
for (int i = 0; i < 1 << N - 1; ++i) {
int sum = 1;
for (int j = 2; j <= N; ++j) {
sum += (i >> N - j & 1 ? -j : j);
}
num += (sum == M);
}
if (num) {
ma[N * 100 + M] = num;
}
}
}
for (int N, M; cin >> N >> M;) {
if (ma.find(N * 100 + M) != ma.end()) {
cout << ma[N * 100 + M] << endl;
}
else {
cout << "No\n";
}
}
}