【ACM】C++程序设计ACM题库总结

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<intint> 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,只占用一位。元素仅包含两个可能的值:01 (truefalse)2. Bitset能够由整型值或由01构成的字符串构建,并可以转换成整型值或由01构成的字符串。
        
	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不是完数,因为241+2+3+4+6+8+1236。
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 从 21000):
        先计算尾部 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";
		}
	}
}
  • 9
    点赞
  • 97
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值