函数
如同我们数学中学的 f(x) = ax + b ,函数就是把一个东西丢进去,然后进行类似的操作变化,最终得到的可以是一个数,也可能什么都得不到而只是进行一项操作。
如sqrt() , max() 和 swap() 这样的其实都是函数,我们写的 int main() 其实也是函数,名为 主函数 。这里主要讲的是手写函数。
题目描述
给出平面坐标上不在一条直线上三个点坐标 (x1,y1),(x2,y2),(x3,y3)(x_1,y_1),(x_2,y_2),(x_3,y_3)(x1,y1),(x2,y2),(x3,y3),坐标值是实数,且绝对值不超过 100.00,求围成的三角形周长。保留两位小数。
对于平面上的两个点 (x1,y1),(x2,y2)(x_1,y_1),(x_2,y_2)(x1,y1),(x2,y2),则这两个点之间的距离 dis=(x2−x1)2+(y2−y1)2dis=\sqrt{(x_2-x_1)^2+(y_2-y_1)^2}dis=(x2−x1)2+(y2−y1)2
输入格式
输入三行,第 iii 行表示坐标 (xi,yi)(x_i,y_i)(xi,yi),以一个空格隔开。
输出格式
输出一个两位小数,表示由这三个坐标围成的三角形的周长。
样例输入
0 0
0 3
4 0
样例输出
12.00
数据范围
数据保证,坐标均为实数且绝对值不超过 100100100,小数点后最多仅有 333 位。
直接写出来的话就是:
#include<bits/stdc++.h>
using namespace std;
int main()
{
double x1 , y1 , x2 , y2 , x3 , y3 , ans = 0 ;
scanf("%lf%lf%lf%lf%lf%lf" ,&x1 ,&y1 ,&x2 ,&y2 ,&x3 ,&y3) ;
ans += sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) ;
ans += sqrt((x3 - x2) * (x3 - x2) + (y3 - y2) * (y3 - y2)) ;
ans += sqrt((x3 - x1) * (x3 - x1) + (y3 - y1) * (y3 - y1)) ;
printf("%.2lf" ,ans) ;
return 0 ;
}
看起来非常丑陋,但是我们可以通过手写函数的方法来解决代码操作重复的问题。
#include<bits/stdc++.h>
using namespace std;
double dis(double a1 ,double b1 ,double a2 ,double b2)
{
return sqrt((a2 - a1) * (a2 - a1) + (b2 - b1) * (b2 - b1)) ;
}
int main()
{
double x1 , y1 , x2 , y2 , x3 , y3 , ans = 0 ;
scanf("%lf%lf%lf%lf%lf%lf" ,&x1 ,&y1 ,&x2 ,&y2 ,&x3 ,&y3) ;
ans += dis(x2 ,y2 ,x1 ,y1) ;
ans += dis(x3 ,y3 ,x2 ,y2) ;
ans += dis(x3 ,y3 ,x1 ,y1) ;
printf("%.2lf" ,ans) ;
return 0 ;
}
这样是不是就简短多了,类比我们的 f(x) = ax + b ,那么此时的 f(1) 是不是就等价于 a + b 了,同理当我们定义 dis 为
double dis(double a1 ,double b1 ,double a2 ,double b2)
{
return sqrt((a2 - a1) * (a2 - a1) + (b2 - b1) * (b2 - b1)) ;
}
时,dis(x2 ,y2 ,x1 ,y1) ; 就等价于 sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) ,相当于把 x2 ,y2 ,x1 ,y1 代入到了这个函数里,得到其中return 的结果,此时我们用 double 声明这个函数,因此返还的结果为 浮点型 ,若使用 int 声明函数,则返回值为 整形 。
题目描述
输入 nnn 个不大于 10510^5105 的正整数。要求全部储存在数组中,去除掉不是质数的数字,依次输出剩余的质数。
输入格式
第一行输入一个正整数 nnn,表示整数个数。
第二行输入 nnn 个正整数 aia_iai,以空格隔开。
输出格式
输出一行,依次输出 aia_iai 中剩余的质数,以空格隔开。
样例输入
5
3 4 5 6 7
样例输出
3 5 7
数据范围
数据保证,1≤n≤1001\le n\le1001≤n≤100,1≤ai≤1051 \leq a_i \leq 10^51≤ai≤105。
#include<bits/stdc++.h>
using namespace std;
bool panduan(int x)
{
if(x == 1) return 0 ;//对1进行特判
for(int i = 2 ; i <= sqrt(x) ; i ++)
if(x % i == 0) return 0 ;//判断从2到根号x有无x的因子
return 1 ;
}
int main()
{
int n , a ;
scanf("%d" ,&n) ;
for(int i = 1 ; i <= n ; i ++)
{
scanf("%d" ,&a) ;
if(panduan(a)) printf("%d " ,a) ;
}
return 0 ;
}
这里使用了 bool 型的函数, bool 在这里可以改为用 int , bool 声明的变量只能存 000 和 111 ,可以理解为存储范围为 000 到 111 的一个整型,在 bool 声明的函数中 return 0 并不像主函数里那样意味着程序的结束运行,而是表示返还 000 ,这里的 000 和 111 改成 falsefalsefalse 和 truetruetrue 也是可以的,就像:
bool panduan(int x)
{
if(x == 1) return false ;//对1进行特判
for(int i = 2 ; i <= sqrt(x) ; i ++)
if(x % i == 0) return false ;//判断从2到根号x有无x的因子
return true ;
}
这里我没有搞一个数组先把 a[i]a[i]a[i] 存下来,而是每次读入一个 aaa 就判断是否为质数,要理解评测机的运行规则,可以把输入和输出当成两个文件,对于一组测试数据,所有的输入都放在一起,所有的输出也都放在一起,输入输出是分开的,所以一遍输入一边输出是非常可行的,最后将你的输出结果和标准答案进行对比,如果一样则结果正确。
另外一点很重要的是这里判断是否为素数时,利用 试除法 ,即对一个数 iii 进行取余运算,若没有余数,则说明 iii 是这个数的因数,既然有因数了,就说明不是素数。
理解为什么 试除法 是从 222 判断到 n\sqrt nn ,举例对于 161616 ,16=4\sqrt{16} = 416=4 ,161616 的因子有 1,2,4,8,161,2,4,8,161,2,4,8,16 ,既然是因子,就一定是至少需要两个数相乘(本身除外),比如你知道了 111 是 161616 的因子,那么就可以想到另一个因子是 16/1=1616 / 1 = 1616/1=16 ;知道 222 是 161616 的一个因子,就可以知道 16/2=816/2=816/2=8 是 161616 的一个因子,不难看出,对于一个数 nnn ,它的因子总在 n\sqrt nn 成一个一个对应,还以 161616 为例子就是 111 对应 161616 ,222 对应 888 ,444 对应 444 。
这样我们在判断是否有因子的时候就可以将时间复杂度从 O(n)O(n)O(n) 降低到 O(n)O(\sqrt n)O(n) ,大大缩短运行时间,减少 TLETLETLE 的出现。
bool 函数的其他用法
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a[10] ;
for(int i = 1 ; i <= 5 ; i ++)
scanf("%d" ,&a[i]) ;
sort(a + 1 , a + 1 + 5) ;
for(int i = 1 ; i <= 5 ; i ++)
printf("%d " ,a[i]) ;
return 0 ;
}
在 c++ 中有一个内置函数 sort ,其作用是将数组按照从小到大排序,如上格式,将数组从 a[1]a[1]a[1] 到 a[5]a[5]a[5] 进行排序,可以写成 sort(a + 1 , a + 1 + 5) ; ,理解的话就理解为对于数组 aaa ,使用时候下标是从 000 开始的,因此想排序从 111 到 NNN 要写成 sort(a + 1 , a + 1 + N) ; 这样的形式。
如果希望从小到大排序,可以声明一个 boolboolbool 型变量:
bool mycmp(int x , int y)
{
return x > y ;
}
使用时写成:
#include<bits/stdc++.h>
using namespace std;
bool mycmp(int x , int y)
{
return x > y ;
}
int main()
{
int a[10] ;
for(int i = 1 ; i <= 5 ; i ++)
scanf("%d" ,&a[i]) ;
sort(a + 1 , a + 1 + 5 , mycmp) ;
for(int i = 1 ; i <= 5 ; i ++)
printf("%d " ,a[i]) ;
return 0 ;
}
void 声明的函数
voidvoidvoid 声明的函数没有返回值,但还是要写 return ; 。
题目描述
因为 151151151 既是一个质数又是一个回文数(从左到右和从右到左是看一样的),所以 151151151 是回文质数。
写一个程序来找出范围 [a,b](5≤a<b≤100,000,000)[a,b] (5 \le a < b \le 100,000,000)[a,b](5≤a<b≤100,000,000)(一亿)间的所有回文质数。
输入格式
第一行输入两个正整数 aaa 和 bbb。
输出格式
输出一个回文质数的列表,一行一个。
样例输入
5 500
样例输出
5
7
11
101
131
151
181
191
313
353
373
383
提示
Hint 1: Generate the palindromes and see if they are prime.
提示 1: 找出所有的回文数再判断它们是不是质数(素数).
Hint 2: Generate palindromes by combining digits properly. You might need more than one of the loops like below.
提示 2: 要产生正确的回文数,你可能需要几个像下面这样的循环。
题目翻译来自NOCOW。
USACO Training Section 1.5
产生长度为 555 的回文数:
for (d1 = 1; d1 <= 9; d1+=2) { // 只有奇数才会是素数
for (d2 = 0; d2 <= 9; d2++) {
for (d3 = 0; d3 <= 9; d3++) {
palindrome = 10000*d1 + 1000*d2 +100*d3 + 10*d2 + d1;//(处理回文数...)
}
}
}
这里分享一个洛谷 888888 分的做法:
#include<bits/stdc++.h>
using namespace std;
int v[10000010] , pri[10000010] , a[20] , tot , n , m ;
void primes(int x)
{
memset(v , 0 , sizeof(v)) ;//v[i]所存的数表示i的最小质因子
tot = 0 ; //记录质数的数量,对于数组来说相当于一个箭头
for(int i = 2 ; i <= x ; i ++)
{
if(v[i] == 0)//v[i]为0说明在这之前i没有因子,即i为质数
{
v[i] = i ;
pri[++ tot] = i ;
}
for(int j = 1 ; j <= tot ; j ++)
{
if(pri[j] > v[i] || pri[j] > x / i) break ;
//如果i有比pri[j]更小的质因子或超出n的范围,则停止循环
v[i * pri[j]] = pri[j] ;
//pri[j]是i * pri[j] 的最小质因子
}
}
return ;
}
bool huiwen(int x)
{
int len = 0 ;
while(x)
{
a[++ len] = x % 10 ;
x /= 10 ;
}
for(int i = 1 , j = len ; i < j ; i ++ , j --)
if(a[i] != a[j]) return false ;
return true ;
}
int main()
{
scanf("%d%d" ,&n ,&m) ;
primes(m) ;
for(int i = 1 ; i <= tot ; i ++)
{
if(pri[i] > m) break ;
if(pri[i] >= n && huiwen(pri[i])) printf("%d\n" ,pri[i]) ;
}
return 0 ;
}
由于题中数据范围到 10810^{8}108 ,数组 vvv 也至少声明这么大,但是会导致爆空间 MLEMLEMLE ,但不开这么大则会造成运行时错误 RERERE 。
结构体
题目描述
有 nnn 名同学,每名同学有语文、数学、英语三科成绩,你需要按照如下规则对所有同学的成绩从高到低排序:
- 比较总分,高者靠前;
- 如果总分相同,则比较语文和数学两科的总分,高者靠前;
- 如果仍相同,则比较语文和数学两科的最高分,高者靠前;
- 如果仍相同,则二人并列。
你需要输出每位同学的排名,如遇 xxx 人并列,则他们排名相同,并留空后面的 x−1x - 1x−1 个名次。例如,有 333 名同学并列第 111,则后一名同学自动成为第 444 名。
输入格式
第一行一个整数 NNN,表示同学的人数。
接下来 NNN 行,每行三个非负整数 ci,mi,eic_i, m_i, e_ici,mi,ei 分别表示该名同学的语文、数学、英语成绩。
输出格式
输出 NNN 行,按输入同学的顺序,输出他们的排名。
注意:请不要按排名输出同学的序号,而是按同学的顺序输出他们各自的排名。
样例输入
6
140 140 150
140 149 140
148 141 140
141 148 140
145 145 139
0 0 0
样例输出
1
3
4
4
2
6
数据范围
- 对 30%30\%30% 的数据,N≤100N \leq 100N≤100,且所有同学总分各不相同。
- 对全部的测试数据,保证 2≤N≤1042 \leq N \leq 10^42≤N≤104,0≤ci,mi,ei≤1500 \leq c_i, m_i, e_i \leq 1500≤ci,mi,ei≤150。
#include<bits/stdc++.h>
using namespace std;
int n ;
struct stu
{
int c , z , e , m , zg , cm , id , rank ;
}t[100010];
bool mycmp1(stu x , stu y)
{
if(x.z != y.z) return x.z > y.z ;
else if(x.cm != y.cm) return x.cm > y.cm ;
else if(x.zg != y.zg) return x.zg > y.zg ;
else return x.id < y.id ;
}
bool mycmp2(stu x , stu y)
{
return x.id < y.id ;
}
int main()
{
scanf("%d" ,&n) ;
for(int i = 1 ; i <= n ; i ++)
{
scanf("%d%d%d" ,&t[i].c ,&t[i].m ,&t[i].e) ;
t[i].z = t[i].c + t[i].m + t[i].e ;
t[i].cm = t[i].c + t[i].m ;
t[i].zg = max(t[i].c , t[i].m) ;
t[i].id = i ;
}
sort(t + 1 , t + 1 + n , mycmp1) ;
t[1].rank = 1 ;
for(int i = 2 ; i <= n ; i ++)
{
if(t[i].z == t[i - 1].z && t[i].cm == t[i - 1].cm && t[i].zg == t[i - 1].zg)
t[i].rank = t[i - 1].rank ;
else t[i].rank = i ;
}
sort(t + 1 , t + 1 + n , mycmp2) ;
for(int i = 1 ; i <= n ; i ++)
printf("%d\n" ,t[i].rank) ;
return 0 ;
}
类比我们用 int 和 double 等声明不同类型的变量,结构体 也可理解为我们新定义了一种新的数据类型,可以用 stu 声明这个变量,其中包含了 c , z ,m ... 这样的元素,因此 结构体 的声明也可以写成:
struct stu
{
int c , z , e , m , zg , cm , id , rank ;
};
stu t[100010] ;

被折叠的 条评论
为什么被折叠?



