中学信息奥赛course

C++

  • 面向对象
  • 万能头文件<bits/stdc++.h>
  • 常用命名空间using namespace std;
  • 输入:cin>>
  • 输出:cout<< ;cout<<endl(换行)

1.STL

顺序表的删除(vector, list)

vector

Description
实现一个线性表:参照sq_delete函数,对一个n不超过2^10的线性表进行删除操作。

Input
第一行有一个整数n,表示线性表的大小,第二行有n个整数,分别是list1,list2…listn。
第三行有一个整数q,表示q次删除操作,接下来q行,每行有一个整数k,表示删除线性表中第k个元素。
(输入有多组测试数据,以EOF结束)

Output
对于每次删除操作输出一行,如果k不合法,输出 -1, 否则输出删除的元素。

Sample Input
5
3 2 1 5 4
3
5
5
2

Sample Output
4
-1
2

#include<bits/stdc++.h> 
using namespace std;
int main(){
	vector<int> ve;
	int n,i,a,q,k;
	while(scanf("%d",&n)!=EOF){ //多组测试数据输入
		ve.clear();
		for(i=0;i<n;i++)
		{
			scanf("%d",&a);
			ve.push_back(a);//在容器的最后添加一个数据 
		}
		vector<int>::iterator it;//定义一个迭代器
		scanf("%d",&q);
		for(i=0;i<q;i++){
			scanf("%d",&k);
			if(k > ve.size())
			{
				printf("-1\n");
			}
			else{
				it = ve.begin();
				for(int j = 0;j < k-1;j++)//指针指向k位置 
				{
					it++;
				}
				printf("%d\n",*it);
					ve.erase(it);
			}
		}
		 
	}
	return 0;	
}

list

#include<bits/stdc++.h> 
using namespace std;
int main(){
	int n,k,q,a;
	list<int> li;
	while(scanf("%d",&n)!=EOF)
	{
		li.clear();
		for(int i=0;i<n;i++)
		{
			scanf("%d",&a);
			li.push_back(a);
		}
		scanf("%d",&q);
		
		list<int>::iterator it;
		for(int i = 0;i < q;i++){
			scanf("%d",&k);
			if(k>li.size())printf("-1\n");
			else{
				it=li.begin();
				for(int j = 0 ;j < k-1;j++)
				{
					it++;
				}printf("%d\n",*it);
				li.erase(it);
			}
		}
			
			}	
			return 0;
}

  • 需要高效的随机存取而不再乎中间位置的插入删除->vector
  • 需要大量中间位置的插入删除而不关心随机存取->list
  • 需要随机存取而且关心两端数据的操作->deque

{A} + {B} (set)

set

  • set中的元素已经从小到大排好序,采用平衡树来存储,复杂度logn,其中元素可以是任意类型的。
  • 基本操作:
    begin() 返回指向第一个元素的迭代器
    empty() 如果集合为空,返回true
    erase() 删除集合中的元素
    find() 返回一个指向或查找到元素的迭代器
    lower_bound() 返回指向大于(或等于)某值的第一个元素的迭代器
  • example:
#include<iostream>
#include<set>
using namespace std;
int main(){
	set<int> s;
	set<int>::iterator it;  //创建一个对应的迭代器 
	s.insert(1);
	s.insert(2);
	s.insert(3);
	s.insert(1);
	cout<<s.count(1)<<endl;
	cout<<s.count(4)<<endl;
	it=s.find(2);
	if(it!=s.end())
	  cout<<*it<<endl;
	return 0;
} 

Description
给你两个集合,要求{A} + {B}.
注:同一个集合中不会有两个相同的元素.

Input
每组输入数据分为三行,第一行有两个数字n,m(0 < n,m <=10000),分别表示集合A和集合B的元素个数.后两行分别表示集合A和集合B.每个元素为不超出int范围的整数,每个元素之间有一个空格隔开.

Output
针对每组数据输出一行数据,表示合并后的集合,要求从小到大输出,每个元素之间有一个空格隔开.

Sample Input
1 2
1
2 3
1 2
1
1 2

Sample Output
1 2 3
1 2

#include<iostream>
#include<set>
using namespace std;
int main(){
	int n,m,value;
	set<int> res;
	set<int>::iterator it;
	while(cin>>n>>m){
		res.clear();
			bool flag=false;
		for(int i=1;i<=n+m;i++){
			cin>>value;
			res.insert(value);
		}
		for(it=res.begin();it!=res.end();it++){
			if(flag)cout<<" "; //除了第一个其他都在输出前输出一个空格 
			flag=true;
			cout<<*it;
		}
		cout<<endl;
	} 
	return 0;
} 

统计数字(map)

map

记录<key,value>形式元素的容器,元素按key排序
定义map<int ,string>mp;
example:


Description
某次科研调查时得到了n个自然数,每个数均不超过1500000000(1.5*10^9)。已知不相同的数不超过10000个,现在需要统计这些自然数各自出现的次数,并按照自然数从小到大的顺序输出统计结果。

Input
第1行是整数n,表示自然数的个数。
第2~n+1行每行一个自然数。

Output
包含m行(m为n个自然数中不相同数的个数),按照自然数从小到大的顺序输出。每行输出两个整数,分别是自然数和该数出现的次数,其间用一个空格隔开。

Sample Input
8
2 4 2 4 5 100 2 100

Sample Output
2 3
4 2
5 1
100 2

#include<iostream>
#include<map> 
#include<string>
using namespace std;
map<int,int> mp;
map<int,int>::iterator it;
int main(){
	int n,a,t;
	while(cin>>n){
		mp.clear();
		for(int i=0;i<n;i++){
			cin>>a;
			mp[a]++;
		}
		for(it=mp.begin();it!=mp.end();it++){
			cout<<it->first<<" "<<it->second<<endl;
		}
	}
	return 0;
} 

ACboy 再次需要你的帮助(stack, queue)

容器适配器

stack<string> s;
stack几个主要函数:
s.empty()堆栈为空则返回真
s.pop()移除栈顶元素(不会返回栈顶元素的值)
s.push(x)在栈顶增加元素
s.size()返回栈中元素数目
s.top()返回栈顶元素

Description
对于每一个样例,第一行是N和一个字符串”FIFO”或者”FILO”(FIFO表示先进先出,即队列;FILO表示先进后出,即栈,),N表示命令的个数,下面有N行,每一行表示一个命令。命令分为两种,IN a 表示进去一个a,OUT表示出一个对头元素或者栈顶元素。

Input
第一行是一个数字T,表示样例的个数,对于每一个样例,如题目所述。

Output
对于每一个OUT命令,你要根据”FIFO”和”FILO”单独一行输出一个数字,或者输出None如果没有整数了。

Sample Input
4
4 FIFO
IN 1
IN 2
OUT
OUT
4 FILO
IN 1
IN 2
OUT
OUT
5 FIFO
IN 1
IN 2
OUT
OUT
OUT
5
FILO
IN 1
IN 2
OUT
IN 3
OUT

Sample Output
1
2
2
1
1
2
None
2
3

#include<bits/stdc++.h>
using namespace std;
int main(){
	int T,n,m;
	char c[5],op[5];
	cin>>T;
	while(T--){
		stack<int> s;
		queue<int> q;
		cin>>n>>c;
		if(c[2]=='L'){  
			for(int i=0;i<n;i++){
				cin>>op;
				if(op[0]=='I'){
			     	cin>>m;
					 s.push(m);
				} 
				else if(op[0]=='O'&&!s.empty()){
					cout<<s.top()<<endl;
					s.pop();
				}
				else cout<<"None"<<endl;
			}
		}
		if(c[2]=='F'){
			for(int i=0;i<n;i++){
				cin>>op;
				if(op[0]=='I'){
			     	cin>>m;
					 q.push(m);
				} 
				else if(op[0]=='O'&&!q.empty()){
					cout<<q.front()<<endl;
					q.pop();
				}
				else cout<<"None"<<endl;
			}
		}
	}
	return 0;
} 

栈的操作(stack)

Description
栈是一种线性数据结构,它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。
对输入整数序列1 2 3 …执行一组栈操作,输出操作的出栈序列。

Input
每行是一个测试用例,表示一个操作序列。操作序列由P和Q两个符号组成,P表示入栈,Q表示出栈。每个操作序列长度不超过1000。

Output
对每个操作序列,输出出栈序列,若操作序列有错误,如栈空时执行出栈操作,输出error,且终止程序。 出栈序列之间用一个空格隔开,末尾没有多余的空格!

Sample Input
PQPPQQPPPQPQ
PPPPQQP
PP
PQQPP
PPQQ

Sample Output
1 3 2 6 7
4 3
`
1 error
2 1

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,count;
	string str;
	stack<int> st;
	while(cin>>str){
		n=1;
		count=str.find_last_of('Q');
		for(int i=0;i<str.length();i++){
			if(str[i]=='P'){
				st.push(n++);
			}
			else{
				if(st.empty()){
					cout<<"error";
					break;
				}
				else{
					cout<<st.top();
					st.pop();
				}
				if(i!=count)cout<<" ";
			}
		}
		//重置 
	while(!st.empty()){
		st.pop();  
	}
	cout<<endl;
	}
	
    return 0;
}

在这里插入图片描述

看病要排队(priority_queue)

priority_queue

默认权值最高者排在最前面

Description
看病要排队这个是地球人都知道的常识。
不过经过细心的0068的观察,他发现了医院里排队还是有讲究的。0068所去的医院有三个医生(汗,这么少)同时看病。而看病的人病情有轻重,所以不能根据简单的先来先服务的原则。所以医院对每种病情规定了10种不同的优先级。级别为10的优先权最高,级别为1的优先权最低。医生在看病时,则会在他的队伍里面选择一个优先权最高的人进行诊治。如果遇到两个优先权一样的病人的话,则选择最早来排队的病人。
现在就请你帮助医院模拟这个看病过程。

Input
输入数据包含多组测试,请处理到文件结束。
每组数据第一行有一个正整数N(0 < N < 2000)表示发生事件的数目。
接下来有N行分别表示发生的事件。
一共有两种事件:
1:“IN A B”,表示有一个拥有优先级B的病人要求医生A诊治。(0 2:“OUT A”,表示医生A进行了一次诊治,诊治完毕后,病人出院。(0

Output
对于每个"OUT A"事件,请在一行里面输出被诊治人的编号ID。如果该事件时无病人需要诊治,则输出"EMPTY"。
诊治人的编号ID的定义为:在一组测试中,"IN A B"事件发生第K次时,进来的病人ID即为K。从1开始编号。

Sample Input
7
IN 1 1
IN 1 2
OUT 1
OUT 2
IN 2 1
OUT 2
OUT 1
2
IN 1 1
OUT 1

Sample Output
2
EMPTY
3
1
1

#include<bits/stdc++.h>
using namespace std;
struct Node{
		int id;
		int priority;
		Node(int i,int p){
			id=i;
			priority=p;
		}
		//通过自定义operator<操作符来比较元素中的优先级
		bool operator<(const Node &b) const{
		if(priority==b.priority)return id>b.id; //如果优先级一致,按照先来后到 
		else return priority < b.priority;  //不然大的优先 
		}
	};
	
int main(){
	int n;
	while(scanf("%d",&n)!=EOF){
		priority_queue<Node> pq[4];
		int t=1;
		while(n--){
			string str;
			int a,b;
			cin>>str>>a;
			if(str=="IN"){
				cin>>b;
				pq[a].push(Node(t,b));
				t++;
			}
			else if(str=="OUT"){
				if(pq[a].size()){
					Node e=pq[a].top();
					pq[a].pop();
					cout<<e.id<<endl;
				}
				else cout<<"EMPTY"<<endl;
			}
		}
	}
	return 0;
} 

快速排序——中级(sort)

Description
输入n个数,对其进行从小到大排序。

Input
有多组测试数据,每组以n(n<=1000000)开始 第二行开始输入n个整数,整数均可用int类型表示。数据以n=-1结束。

Output
输出每组测试数据组数,排序数据个数以及排序后从小到大的整数序列。最后一个数后没有多于空格。

Sample Input
5
5 4 3 2 1
10
4 6 8 7 5 1 3 9 2 0
-1

Sample Output
Case number:1
Number of elements:5
1 2 3 4 5
Case number:2
Number of elements:10
0 1 2 3 4 5 6 7 8 9

#include<bits/stdc++.h>
using namespace std;
int n,a[1000010];
int t=1; 
int main(){
	while(cin>>n && n!=-1){
		cout<<"Case number:"<<t<<endl;t++;
		cout<<"Number of elements:"<<n<<endl;
		for(int i=0;i<n;i++)cin>>a[i];
		sort(a,a+n);
		for(int i=0;i<n-1;i++)cout<<a[i]<<" ";
		cout<<a[n-1]<<endl;
	}
	return 0;
}

三级排序(sort)

Description
给你三维坐标(x, y, z),请给三维坐标排序,优先按x值从小到大排序,当x值一样时,按y值从小到大排序,当y值一样时,按z值从小到大排序。

Input
输入数据的第一行是一个数据T,表示有T组数据。
对于每组数据先输入n (1 < n < 50000),表示有n个三维坐标,然后是n行,每行输入三个整数x, y, z。

Output
对于每组输入数据,先输出n,然后输出n行,为排序后的坐标。

Sample Input
2
2
1 2 3
3 2 1
4
2 3 4
2 2 3
2 3 2
3 4 1

Sample Output
2
1 2 3
3 2 1
4
2 2 3
2 3 2
2 3 4
3 4 1

#include <bits/stdc++.h>
using namespace std;
int n,T;
struct three{int x,y,z;}a[50010];
bool cmp(three x,three y){
	if(x.x==y.x&&x.y==y.y)return x.z<y.z;
	if(x.x==y.x)return x.y<y.y;
	return x.x<y.x;
}

int main(){
	cin>>T;
	while(T--){
		cin>>n;
		cout<<n<<endl;
		for(int i=0;i<n;i++)cin>>a[i].x>>a[i].y>>a[i].z;
		sort(a,a+n,cmp);
		for(int i=0;i<n;i++)cout<<a[i].x<<" "<<a[i].y<<" "<<a[i].z<<endl;
	}
	return 0;
}

排列(next_permutation)

Description
1,2,3,…n是自然数1到n的最小的排列,易知,第二小的排列是:1,2,3…n,n-1。现在给你2个值n和m,请找到1到n个数的第m小的排列。

Input
输入包括多组样例,对于每组样例包括两个整数n和m(1<=n<=1000,1<=m<=10000000),分别如题目所述

Output
输出第m小的排列的后各个数字,中间用空格分开,每一个样例占一行

Sample Input
6 4
11 8

Sample Output
1 2 3 5 6 4
1 2 3 4 5 6 7 9 8 11 10

#include <bits/stdc++.h>
using namespace std;
int a[1000010];  //过大的数组要设为全局变量 ,不然栈空间溢出 
int main(){
	int n,m;
	while(cin>>n>>m){
		for(int i=0;i<n;i++)
		  a[i]=i+1;
		for(int i=1;i<m;i++)
		  next_permutation(a,a+n);
		for(int i=0;i<n-1;i++)
	          printf("%d ",a[i]);
		printf("%d\n", a[n-1]);
	}  
	return 0;
}

2.递推算法

以已知条件,按照递推关系,依次推出中间结果及最后结果。

确定递推变量
|
v
建立递推关系
|
v
确定初始(边界)条件
|
v
由初始条件依据递推公式计算后续状态


菲波那契数列(2)

Description
菲波那契数列是指这样的数列: 数列的第一个和第二个数都为1,接下来每个数都等于前面2个数之和。
给出一个正整数a,要求菲波那契数列中第a个数对1000取模的结果是多少。

Input
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数a(1 ≤ a ≤ 1000000)。

Output
n行,每行输出对应一个输入。输出应是一个正整数,为菲波那契数列中第a个数对1000取模得到的结果。

Sample Input
4
5
2
19
1

Sample Output
5
1
181
1

#include<bits/stdc++.h>
using namespace std;
int main(){
	int a,b,c,m,n;
	cin>>n;
	while(n--){
    	a=1;
		b=1;
	  cin>>m;
	  if(m<3)cout<<1<<endl;
	  else{
	  	for(int i=1;i<m-1;i++){
	  		c=a+b;
	  		a=b;
	  		b=c;
	  	}
	  	cout<<c%1000<<endl;
	  }	
	}
	return 0;
} 

Pell数列

Description
Pell数列a[1],a[2],a[3], …的定义是这样的,a[1] = 1, a[2] = 2, … , a[n] = 2*a[n−1] + a[n-2] (n>2)。
给出一个正整数k,要求Pell数列的第k项模上32767是多少。

Input
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数k (1≤k<1000000)。

Output
n行,每行输出对应一个输入。输出应是一个非负整数。

Sample Input
2
1
8

Sample Output
1
408

#include<bits/stdc++.h>
using namespace std;
int a[1000];
int main(){
	int n,c,b,res;
	cin>>n;
		for(int i=0;i<n;i++){
			cin>>a[i];
	  if(a[i]==1 || a[i]==2)cout<<a[i]<<endl;
    	else{
		c=1;
		b=2;
		for(int j=2;j<a[i];j++){
			res=(c+2*b)%32767;
			c=b;
			b=res;
		}
		cout<<res<<endl;
	}
		}	
	return 0;
}

上台阶

Description
楼梯有n(71>n>0)阶台阶,上楼时可以一步上1阶,也可以一步上2阶,也可以一步上3阶,编程计算共有多少种不同的走法。

Input
输入的每一行包括一组测试数据,即为台阶数n。最后一行为0,表示测试结束。

Output
每一行输出对应一行输入的结果,即为走法的数目。

Sample Input
1
2
3
4
0

Sample Output
1
2
4
7

#include<bits/stdc++.h>
using namespace std;
long long d[80]={0};
int n;
int main(){
	d[1]=1;
	d[2]=2;
	d[3]=4;
	for(int i=4;i<=71;i++){
		d[i]=d[i-1]+d[i-2]+d[i-3];
	}
	while(scanf("%d",&n)!=EOF){
		if(n!=0){
			cout<<d[n]<<endl;
		}
	}
	return 0;
} 

爬楼梯

Description
树老师爬楼梯,他可以每次走1级或者2级,输入楼梯的级数,求不同的走法数。
例如:楼梯一共有3级,他可以每次都走一级,或者第一次走一级,第二次走两级,也可以第一次走两级,第二次走一级,一共3种方法。

Input
输入包含若干行,每行包含一个正整数N,代表楼梯级数,1≤N≤30。

Output
不同的走法数,每一行输入对应一行输出。

Sample Input
5
8
10

Sample Output
8
34
89

#include<bits/stdc++.h>
using namespace std;
int fun(int n){
	if(n==1)return 1;
	else if(n==2)return 2;
	else return fun(n-1)+fun(n-2);
}
int main(){
	int n;
	while(scanf("%d",&n)!=EOF){
		cout<<fun(n)<<endl;
	}
	return 0;
} 

骨牌铺方格——中级

Description
在2×n的一个长方形方格中,用一个1× 2的骨牌铺满方格,输入n ,输出铺放方案的总数.
例如n=3时,为2× 3方格,骨牌的铺放方案有三种,如下图:
在这里插入图片描述

Input
输入数据由多行组成,每行包含一个整数n,表示该测试实例的长方形方格的规格是2×n (0<n<=50)。

Output
对于每个测试实例,请输出铺放方案的总数,每个实例的输出占一行。

Sample Input
1
3
2

Sample Output
1
3
2

数学关系式与上题一样

#include<bits/stdc++.h>
using namespace std;
int fun(int n){
	if(n==1)return 1;
	else if(n==2)return 2;
	else return fun(n-1)+fun(n-2);
}
int main(){
	int n;
	while(scanf("%d",&n)!=EOF){
		cout<<fun(n)<<endl;
	}
	return 0;
} 

昆虫繁殖

Description
科学家在热带森林中发现了一种特殊的昆虫,这种昆虫的繁殖能力很强。每对成虫过x个月产y对卵,每对卵要过两个月长成成虫。假设每个成虫不死,第一个月只有一对成虫,且卵长成成虫后的第一个月不产卵(过X个月产卵),问过Z个月以后,共有成虫多少对?0≤X≤20,1≤Y≤20,X≤Z≤50。

Input
x,y,z的数值。

Output
过Z个月以后,共有成虫对数。

Sample Input
1 2 8

Sample Output
37

第n月成虫数是上个月成虫数+两个月前的卵,第n月卵数是x月前的成虫数乘以y对。
注意数组要赋初值,默认数组元素是随机数并不是0。

#include<bits/stdc++.h>
using namespace std;
int main(){
	int x,y,z,a[100]={0},b[100]={0};
	while(cin>>x>>y>>z){
		for(int i=1;i<=x;i++){
			a[i]=1;
			b[i]=0;
		}
		for(int i=x+1;i<=z+1;i++){
			a[i]=a[i-1]+b[i-2];
			b[i]=a[i-x]*y;
		}
		cout<<a[z+1]<<endl;
	}
	return 0;
} 

放苹果

Description
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。

Input
第一行是测试数据的数目t(0≤t≤20)。以下每行均包含二个整数M和N,以空格分开。1≤M,N≤10。

Output
对输入的每组数据M和N,用一行输出相应的K。

Sample Input
1
7 3

Sample Output
8

#include<bits/stdc++.h>
using namespace std;
int fun(int m,int n){
	if(n>m)return fun(m,m);
	else if(m==0)return 1;
	else if(n==0)return 0;
	else return fun(m,n-1)+fun(m-n,n);
}
int main(){
	int m,n,t;
	cin>>t;
	while(t--){
		cin>>m>>n;
		cout<<fun(m,n)<<endl;
	}
	return 0;
} 

移动路线

Description
X桌子上有一个m行n列的方格矩阵,将每个方格用坐标表示,行坐标从下到上依次递增,列坐标从左至右依次递增,左下角方格的坐标为(1,1),则右上角方格的坐标为(m,n)。
小明是个调皮的孩子,一天他捉来一只蚂蚁,不小心把蚂蚁的右脚弄伤了,于是蚂蚁只能向上或向右移动。小明把这只蚂蚁放在左下角的方格中,蚂蚁从
左下角的方格中移动到右上角的方格中,每步移动一个方格。蚂蚁始终在方格矩阵内移动,请计算出不同的移动路线的数目。
对于1行1列的方格矩阵,蚂蚁原地移动,移动路线数为1;对于1行2列(或2行1列)的方格矩阵,蚂蚁只需一次向右(或向上)移动,移动路线数也为1……对于一个2行3列的方格矩阵,如下所示:
(2,1) (2,2) (2,3)
(1,1) (1,2) (1,3)
蚂蚁共有3种移动路线:
路线1:(1,1) → (1,2) → (1,3) → (2,3)
路线2:(1,1) → (1,2) → (2,2) → (2,3)
路线3:(1,1) → (2,1) → (2,2) → (2,3)

Input
输入只有一行,包括两个整数m和n(0 < m+n ≤ 20),代表方格矩阵的行数和列数,m、n之间用空格隔开。

Output
输出只有一行,为不同的移动路线的数目。

Sample Input
2 3

Sample Output
3

#include<bits/stdc++.h>
using namespace std; 
int main(){
	int m,n;
	int dp[20][20];
	cin>>m>>n;
	dp[0][0]=1;
	//一行或一列是都只有一种路线 
	for(int i=1;i<m;i++){
		dp[i][0]=1;
	}
	for(int i=1;i<n;i++){
		dp[0][i]=1;
	}
	for(int i=1;i<m;i++){
		for(int j=1;j<n;j++){
			dp[i][j]=dp[i-1][j]+dp[i][j-1];
		}
	}
	cout<<dp[m-1][n-1]<<endl;;
	return 0;
}

过河卒

如图,A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。
guohezu
同时在棋盘上的任一点有一个对方的马(如上图的C点),该马所在的点和所有跳跃一步可达的点称为方马的控制点。例如上图C点上的马可以控制9个点(图中的P1,P2…P8和C)。卒不能通过对方的控制点。
棋盘用坐标表示,A点(0,0)、B点(n, m)(n,m为不超过20的整数,并由键盘输入),同样马 的位置坐标是需要给出的(约定:C≠A,同时C≠B)。现在要求你计算出卒从A点能够到达B点的路径的条数。

Input
B点的坐标(n,m)以及对方马的坐标(X,Y) {不用判错}

Output
一个整数(路径的条数)。

Sample Input
6 6 3 2

Sample Output
17

类似上题,add判断不可走的点就行

#include<bits/stdc++.h>
using namespace std;
long long n,m,x,y,s,dp[20][20];
int main(){
	cin>>n>>m>>x>>y;
	dp[0][0]=1;
	for(int i=1;i<=n;i++){
		int j=0;
		//不重合不走马的日 
		if((i==x&&j==y)||(i==x-1&&j==y-2)||(i==x-1&&j==y+2)||(i==x-2&&j==y-1)||(i==x-2&&j==y+1)||(i==x+1&&j==y-2)||(i==x+1&&j==y+2)||(i==x+2&&j==y-1)||(i==x+2&&j==y+1))continue;
		dp[i][j]=dp[i-1][j]; 
	    
	} 
	for(int j=1;j<=m;j++){
		int i=0;
		if((i==x&&j==y)||(i==x-1&&j==y-2)||(i==x-1&&j==y+2)||(i==x-2&&j==y-1)||(i==x-2&&j==y+1)||(i==x+1&&j==y-2)||(i==x+1&&j==y+2)||(i==x+2&&j==y-1)||(i==x+2&&j==y+1))continue;
		dp[i][j]=dp[i][j-1];
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if((i==x&&j==y)||(i==x-1&&j==y-2)||(i==x-1&&j==y+2)||(i==x-2&&j==y-1)||(i==x-2&&j==y+1)||(i==x+1&&j==y-2)||(i==x+1&&j==y+2)||(i==x+2&&j==y-1)||(i==x+2&&j==y+1))continue;
			dp[i][j]=dp[i-1][j]+dp[i][j-1];
		}
	}
	cout<<dp[n][m]<<endl;
	return 0;
}

踩方格

Description
有一个方格矩阵,矩阵边界在无穷远处。我们做如下假设:
a、每走一步时,只能从当前方格移动一格,走到某个相邻的方格上;
b、走过的格子立即塌陷无法再走第二次;
c、只能向北、东、西三个方向走;
请问:如果允许在方格矩阵上走n步,共有多少种不同的方案。2种走法只要有一步不一样,即被认为是不同的方案。

Input
允许在方格上行走的步数n(n≤20)。

Output
计算出的方案数量。

Sample Input
2

Sample Output
7

对于每个方向,如果向北走则上一步是三个方向的都可以,如果向东(或西)走,则上一步只能是北或东(或西)两个方向来的。

#include<bits/stdc++.h>
using namespace std;
int up[20],le[20],ri[20];
int main(){
	up[1]=1;
	le[1]=1;
	ri[1]=1;
	int n,res;
    while(cin>>n){
    		//边界 
	if(n==1)cout<<3<<endl;
	else{
		for(int i=2;i<=n;i++){
			up[i]=ri[i-1]+le[i-1]+up[i-1];
			ri[i]=le[i-1]+up[i-1];
			le[i]=ri[i];
		}
		res=up[n]+ri[n]+le[n];
		cout<<res<<endl; 
	}
    }

	return 0;
}

位数问题

Description
在所有的N位数中,有多少个数中有偶数个数字3? 由于结果可能很大,你只需要输出这个答案对12345取余的值。

Input
读入一个数N(N < 1000)。

Output
输出有多少个数中有偶数个数字3。

Sample Input
2

Sample Output
73

#include<bits/stdc++.h>
using namespace std;
const int mod=12345;
int n;
int a[1010][2];
//a[i][0]指i位数中有偶数个3,a[i][1]有奇数个3 
int main(){
	cin>>n;
	a[1][1]=1;//1-9,3  
	a[1][0]=8;//1-9,1、2、4、5、6、7、8、9
	for(int i=2;i<=2;i++){
		a[i][1]=(a[i-1][0]+a[i-1][1]*9)%mod;
		a[i][0]=(a[i-1][1]+a[i-1][0]*9)%mod;
	}
	cout<<a[n][0]<<endl;
	return 0;
}

三角形最佳路径问题

Description
如下所示的由正整数数字构成的三角形:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,和最大的路径称为最佳路径。你的任务就是求出最佳路径上的数字之和。
注意:路径上的每一步只能从一个数走到下一层上和它最近的下边(正下方)的数或者右边(右下方)的数。

Input
第一行为三角形高度100≥h≥1,同时也是最底层边的数字的数目。
从第二行开始,每行为三角形相应行的数字,中间用空格分隔。
1 <= 数 <= 100

Output
最佳路径的长度数值。

Sample Input
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

Sample Output
30

a[i][j]存放从i,j出发到n层的最大值

#include<bits/stdc++.h>
using namespace std;
int a[101][101];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=i;j++){
			cin>>a[i][j];
		}
	}
	for(int i=n-1;i>0;i--){
		for(int j=1;j<=i;j++){
			if(a[i+1][j]>=a[i+1][j+1])a[i][j]+=a[i+1][j];
			else a[i][j]+=a[i+1][j+1];
		}
	}
	cout<<a[1][1]<<endl;

	return 0;
}

3.递归

直接或间接调用自身,用递归将问题分解成规模更小的子问题来解决。

汉诺塔问题

Description
约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下、由小到大顺序串着由64个圆盘构成的塔。目的是将最左边杆上的盘全部移到中间的杆上,条件是一次只能移动一个盘,且不允许大盘放在小盘的上面。
这是一个著名的问题,几乎所有的教材上都有这个问题。由于条件是一次只能移动一个盘,且不允许大盘放在小盘上面,所以64个盘的移动次数是:18,446,744,073,709,551,615
这是一个天文数字,若每一微秒可能计算(并不输出)一次移动,那么也需要几乎一百万年。我们仅能找出问题的解决方法并解决较小N值时的汉诺塔,但很难用计算机解决64层的汉诺塔。
假定圆盘从小到大编号为1, 2, …

Input
输入为一个整数(小于20)后面跟三个单字符字符串。
整数为盘子的数目,后三个字符表示三个杆子的编号。

Output
输出每一步移动盘子的记录。一次移动一行。
每次移动的记录为例如 a->3->b 的形式,即把编号为3的盘子从a杆移至b杆。

Sample Input
2 a b c

Sample Output
a->1->c
a->2->b
c->1->b

总结规律的时候视角放宏观一点

#include<bits/stdc++.h>
#define N 1000010
using namespace std;
void fun(int n,char a,char c,char b)
{
    if(n==1)
    {
        printf("%c->%d->%c\n",a,n,b);
    }
    else{
    	fun(n-1,a,b,c);  //将n-1个盘子借助b从柱子a移到柱子c上 
        printf("%c->%d->%c\n",a,n,b);
        fun(n-1,c,a,b);  //将n-1个盘子借助a从柱子c移到柱子b上
    } 
}
int main()
{
    int n;
    char a,b,c;
    cin>>n>>a>>b>>c;
    fun(n,a,c,b);
    return 0;
}

求最大公约数问题

Description
给定两个正整数,求它们的最大公约数。

Input
输入一行,包含两个正整数(<1,000,000,000)。

Output
输出一个正整数,即这两个正整数的最大公约数。

Sample Input
6 9

Sample Output
3

Source
一本通1207

#include<bits/stdc++.h>
using namespace std;
int gcd(int a, int b)//求a与b的最大公约数 
{
    if(a == b)
        return a;
    else if(a > b)
        return gcd(a-b, b);
    else
        return gcd(a, b-a);
}
int main()
{
	int a, b;
	while(cin >> a >> b){
		cout << gcd(a, b) << endl;
	}
	
	return 0;
}

后缀表达式的值

Description
从键盘读入一个后缀表达式(字符串),只含有0-9组成的运算数及加(+)、减(—)、乘(*)、除(/)四种运算符。每个运算数之间用一个空格隔开,不需要判断给你的表达式是否合法。以@作为结束标志。

Input
一个后缀表达式。

Output
一个后缀表达式的值。

Sample Input
16 9 4 3 +*-@

Sample Output
-47

#include<bits/stdc++.h>
using namespace std;
long long num[200];
int main(){
	string str;
	getline(cin,str);
	int len = str.length();
	int n = 0;
	for(int i = 0;i < len - 1;i++){
		if(str[i]=='+'){
			num[n-2]+=num[n-1];
			n--;
		}
		else if(str[i]=='-'){
			num[n-2]-=num[n-1];
			n--;
		}
		else if(str[i]=='*'){
			num[n-2]*=num[n-1];
			n--;
		}
		else if(str[i]=='/'){
			num[n-2]/=num[n-1];
			n--;
		}
		else
		{
			long long x=0;
			while(str[i]!=' '){
				x=x*10+str[i]-'0';
				i++;
			}
			num[n++]=x;
		}
	}
	cout<<num[0]<<endl;
}

4.数论

素数知多少

Description
在给定区间[1,n]中有多少个素数?

Input
每行一个整数n(1<=n<=1000000)

Output
[1,n]之间素数的个数,独立一行

Sample Input
1013
100

Sample Output
170
25

素数筛的应用
如果2是素数,那么2的所有倍数都是合数,把2所有倍数都标记出来,以此类推

#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int prime[N];
bool is_prime[N];
int n;
int fun(){
	int sum=0;
	memset(is_prime,true,sizeof(is_prime));
	is_prime[0]=is_prime[1]=false;
	for(int i=2;i<=n;i++){
		if(is_prime[i]){
			prime[sum++]=i;
			for(int j=i*2;j<=n;j+=i){
				is_prime[j]=false;
			}
			
		}
	}
	return sum;
} 
int main(){
	while(scanf("%d",&n)!=EOF){
		cout<<fun()<<endl;
	}
	return 0;
}

[a,b]区间素数的个数

Description
给定区间[a,b],你知道其中有多少个素数吗?

Input
每行两个正整数a,b (1<a<=b<1000000)

Output
[a,b]之间素数的个数

Sample Input
1 11
11 11111

Sample Output
5
1341

#include <bits/stdc++.h>
using namespace std;
int n;
bool fun(int x);
int main(){
	int a,b,sum;
	while(scanf("%d%d",&a,&b)!=EOF){
		sum=0;
		for(int i=a;i <= b;i++){
			if(fun(i)==true)sum++;
		}
		cout<<sum<<endl;
	}	
	return 0;
} 
bool fun(int x){
	if(x<2)return false;
	for(int i = 2;i <= x/i;i++)
		if(x%i==0)return false;
	return true;
}

分解质因子

Description
在数论里,某一正整数的质因子指能整除该数的质数整数。比如12的质因子有2和3。给定一个正整数n,你知道它的质因子有哪些吗?

Input
每行一个正整数n (1<n<1000000)

Output
n的质因子,从小到大,任意两个之间用一个空格隔开,独立一行

Sample Input
12
210

Sample Output
2 3
2 3 5 7

#include <bits/stdc++.h>
using namespace std;

void divide(int n){
	for(int i=2;i<=n/i;i++){
		if(n%i==0){
			while(n%i==0)n/=i;
		cout<<i<<' ';
		}
	}
	if(n>1)cout<<n;
}
int main(){
	int n;
	while(scanf("%d",&n)!=EOF){
		divide(n);
		cout<<endl;
	}
	return 0;
}

最大公约数

Description
公约数,亦称“公因数”。它是几个整数同时均能整除的整数。如果一个整数同时是几个整数的约数,称这个整数为它们的“公约数”;公约数中最大的称为最大公约数。两个数a,b的最大公约数记为gcd(a,b)。给定任意两个正整数a,b,你知道gcd(a,b)是多少吗?

Input
每行两个正整数a,b

Output
gcd(a,b),独立一行

Sample Input
5 6
20 12

Sample Output
1
4

欧几里得算法

#include <bits/stdc++.h>
using namespace std;
int gcd(int a,int b){
	if(a%b==0)return b;
	else
		return gcd(b,a%b);
}
int main(){
	int a,b,c;
	if(a<b){
		c=a;
		a=b;
		b=c;
	}
	while(scanf("%d%d",&a,&b)!=EOF){
		cout<<gcd(a,b)<<endl;
	}
	return 0;
}

最小公倍数

Description
最小公倍数(Least Common Multiple,缩写L.C.M.),如果有一个自然数a能被自然数b整除,则称a为b的倍数,b为a的约数,对于两个整数a,b来说,指该两数共有倍数中最小的一个,记为lcm(a,b)。

Input
每行两个正整数a,b

Output
lcm(a,b),独立一行

Sample Input
5 6
1 19

Sample Output
30
19

#include <bits/stdc++.h>
using namespace std;
//两个数的乘积会很大,超过int的表示范围
long long gcd(int a,int b){
	if(a%b==0)return b;
	else return gcd(b,a%b);
}
int main(){
	int a,b;
	while(cin>>a>>b){
		cout<<a/gcd(a,b)*b<<endl; 
	}
	return 0;
}

青蛙的约会

Description
两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。

Input
输入只包括一行5个整数x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。

Output
输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行"Impossible"

Sample Input
1 2 3 4 5

Sample Output
4

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x,y,m,n,l;
void ex_gcd(ll a,ll b,ll &x,ll &y,ll &d){
	if(!b){
		d=a;
		x=1;
		y=0;
		return ;
	}
	ex_gcd(b,a%b,y,x,d);
	y-=x*(a/b);
}
int main(){
	while(cin>>x>>y>>m>>n>>l){
		ll dx,dy,gcd;
		ex_gcd(n-m,l,dx,dy,gcd);
		if((x-y)%gcd)cout<<"Impossible"<<endl;
		else{
			ll t=l/gcd;
			dx=(x-y)/gcd*dx;
			cout<<(dx%t+t)%t<<endl;
		}
	}
	return 0;
}

中国剩余定理

Sample Input
3
2 3 2
3 5 7

Sample Output
23

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll ex_gcd(ll a,ll b,ll &x,ll &y){
	if(b==0){
		x=1;
		y=0;
		return a;
	}
	ll ans = ex_gcd(b,a%b,x,y);
	ll tmp=x;
	x=y;
	y=tmp-a/b*y;
	return ans;
}
ll crt(ll a[],ll m[],ll n){
	ll M=1;
	ll ans=0;
	for(ll i=0;i<n;i++)
		M*=m[i];
	for(int i=0;i<n;i++){
		ll x,y;
		ll Mi=M/m[i];
		ex_gcd(Mi,m[i],x,y);
		ans=(ans+a[i]*Mi*x)%M;
	} 
	if(ans<0)ans+=M;
	return ans;
}
int main(){
	ll n,b[10],w[10];
	while(cin>>n){
		for(int i=0;i<n;i++)cin>>b[i];
		for(int i=0;i<n;i++)cin>>w[i];
		cout<<crt(b,w,n)<<endl;
	}
	return 0;
}

5.分治与二分

查找最接近的元素(二分查找)

Description
在一个非降序列中,查找与给定值最接近的元素。

Input
第一行包含一个整数n,为非降序列长度。1 ≤ n ≤ 100000。
第二行包含n个整数,为非降序列各元素。所有元素的大小均在0-1,000,000,000之间。
第三行包含一个整数m,为要询问的给定值个数。1 ≤ m ≤ 10000。
接下来m行,每行一个整数,为要询问最接近元素的给定值。所有给定值的大小均在0-1,000,000,000之间。

Output
m行,每行一个整数,为最接近相应给定值的元素值,保持输入顺序。若有多个值满足条件,输出最小的一个。

Sample Input
3
2 5 8
2
10
5

Sample Output
8
5

#include<bits/stdc++.h>
using namespace std;
#define Max 1000000
int a[Max];
int main(){
	int n,m,x,low,high;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	cin>>m;
	while(m--){
		cin>>x;
		low=1,high=n;
		//low<high将会死循环 
		while(low<high-1) 
		{
			int mid=(low+high)/2;
			if(a[mid]>x)high=mid;
			else low=mid;
		} 
		if(fabs(a[low]-x)>fabs(a[high]-x))cout<<a[high]<<endl;
		else cout<<a[low]<<endl;
	}
	return 0;
} 

取余运算(mod)

Description
输入b,p,k的值,求b^p mod k的值。其中b,p,k为长整型数。

Input
输入b,p,k的值。

Output
求b^p mod k的值。

Sample Input
2 10 9

Sample Output
2^10 mod 9=7

#include<bits/stdc++.h>
using namespace std;
typedef long long int LLI;
LLI fun(LLI b,LLI p,LLI k){
	LLI res;
	if(p==0)return 1;
	res=fun(b,p/2,k)%k;
	res=res*res%k;
	if(p%2==1)res=res*b%k;
	return res;
}
int main(){
	LLI b,p,k;
	cin>>b>>p>>k;
	b%=k;
	cout<<b<<"^"<<p<<" "<<"mod"<<" "<<k<<"="<<fun(b,p,k)<<endl;
	return 0;
} 

简单快速幂

Description
杰哥又遇到难题了,大家快来帮他解决。
这次题目很简单。给你一个整数N,求NN的个位数。
当然,事实上作为学霸,杰哥才不会问如此简单的问题呢。注意这里的N非常大:N(1<=N<=1,000,000,000)。

Input
第一行包含一个正整数T,代表有多少组测试样例。
接下T行中每行有一个正整数N

Output
对于每一组测试样例,输出NN的个位数。

Sample Input
2
3
4

Sample Output
7
6

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL fun(LL a,LL b){
	LL res=1;
	while(b!=0){
	if(b%2==1)res=res*a;
	a=a*a;
	b/=2;	
	}
	return res;
}
int main(){
	LL N,T,ans;
	cin>>T;
	while(T--){
		cin>>N;
		ans=fun(N,N)%10;
		cout<<ans<<endl;
	}
	return 0;
} 
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ca1m4n

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

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

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

打赏作者

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

抵扣说明:

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

余额充值