算法笔记 胡凡.书中的样例源码.
**这些代码都是在Dev C++ 中跑的.可以复制就用上,提供给有算法笔记这本书的同学来测试书中的代码 不过后来我去买yxc的课了. 回头看这些代码有些呆和长 C++ 特性页没用上 所以去买课吧哈哈哈 acwing **
目前只有这些.上个学期课余时间抄录的.难免有一些纰漏.也希望大家帮我挑挑错.
2
2.2.6,常用math函数
#include<cstdio>
#include<math.h>
//变量取绝对值
int test1()
{
double db = -12.56;
printf("%.2f\n", fabs(db));
return 0;
}
//向上取整,向下取整
int test2()
{
double db1 = -5.2, db2 = 5.2;
printf("汉字%.0f %.0f \n", floor(db1), ceil(db1));
printf("%.0f %.0f\n,", floor(db2), ceil(db2));
return 0;
}
//该函数用于返回r的p次幂 求r的p次方
int test3()
{
double db = pow(2.0, 3.0);;
printf("%f\n", db);
return 0;
}
//该函数用于返回double类型的算术平方根.
int test4()
{
double db = sqrt(2.0);
printf("%f\n", db);
return 0;
}
//该函数用于返回double型变量的以自然数对数为底的对数
int test5()
{
double db = log(1.0);
printf("%f\n", db);
return 0;
}
/*
sin(double x),cos(double x),tan(double x) 返回正弦值等,要求是弧度制
asin(double x),acos(double x),atan(double x) 返回反正弦值等,要求是弧度制
*/
const double pi = acos(-1.0);
int test6()
{
double db1 = sin(pi * 45 / 180);
double db2 = cos(pi * 45 / 180);
double db3 = tan(pi * 45 / 180);
printf("%f,%f,%f\n", db1, db2, db3);
return 0;
}
//返回double型变量的反正弦值 ...
int test7()
{
double db1 = asin(1);
double db2 = acos(-1.0);
double db3 = atan(0);
printf("%f,%f,%f\n", db1, db2, db3);
return 0;
}
// 将double类型四舍五入,返回类型也是double 需要进行取整.
int test8()
{
double db1 = round(3.40);
double db2 = round(3.45);
double db3 = round(3.50);
double db4 = round(3.55);
double db5 = round(3.60);
printf("%d, %d, %d, %d, %d\n", (int)db1, (int)db2, (int)db3, (int)db4, (int)db5);
return 0;
}
int main()
{
test8();
}
2.5.2 冒泡排序
//通过n-1轮的排序每次都把最大的挪走到最后一个位置来完成n个元素的排序..
#include<stdio.h>
int main()
{
int a[10] = { 3,1,4,5,2 };
for (int i = 1; i <= 4; i++)//进行n-1趟排序
{
for (int j = 0; j < 5 - i; j++)
{
if (a[j] > a[j + 1])
{
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
for (int i = 0; i < 5; i++)
{
printf("%d", a[i]);
}
return 0;
}
2.5.4 memset
//对数组中每一个元素赋相同的值,仅仅可以赋值0或者-1.其他的值可以用fill函数来赋值.
#include<stdio.h>
#include<string.h>//需要这个头文件.<iostream>里面包括了
int main()
{
int a[5] = { 1,2,3,4,5 };
//赋值0
memset(a, 0, sizeof(a));
for (int i = 0; i < 5; i++)
printf("%d", a[i]);
printf("\n");
//赋值-1
memset(a, -1, sizeof(a));
for (int i = 0; i < 5; i++)
printf("%d", a[i]);
printf("\n");
return 0;
}
2.5.5 字符数组
#include<stdio.h>
//scanf输入printf输出;
int test3() {
char str[10];
char s;
//scanf("%s", str);//%s识别到空格和换行就结束一个字符串输入了%c能够识别空格和换行将其输入.
//printf("%s", str);
scanf("%c", &s);
printf("%c", s);
return 0;
}
//%c的补足.只能是单个字符不能是字符数组.
//getchar输入putchar输出 //用来输入和输出单个字符.
int test4()
{
char str[5][5];
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
str[i][j] = getchar();
}
getchar();//这句未来吧输入中每行末尾的换行符号吸收掉./
}
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++) {
putchar(str[i][j]);
}
putchar('\n');
}
return 0;
}
//gets输入,puts输出/gets是输入一行字符串的.有时候他和scanf有点矛盾.你猜吧
//vs2019里一个源程序文件里若前面有scanf的输入语句时
//后面的gets_s()是不起任何作用的 getchar来吃掉一个换行符号
int test5() {
char str1[20];
char str2[5][10];
gets_s(str1);
for (int i = 0; i < 3; i++)
{
gets_s(str2 [i]);
}
puts(str1);
for (int i = 0; i < 3; i++)
{
puts(str2[i]);
}
return 0;
}
int main()
{
test5();
}
//特别提醒:如果开字符数组.大小要比目的数量多至少一个.
//用getchar 输入字符切记要给结尾增加一个"\0";
2.5.6string.h头文件
#include<stdio.h>
#include<string.h>
//1.strlen()得到字符数组中第一个\0前面的字符的个数
//就是返回.字符串长度
int test6()
{
char str[10];
gets_s(str);
int len = strlen(str);
printf("%d\n", len);
return 0;
}
/*
2.strcmp()比较两个字符串大小通过减法操作.
返回值是负数说明小于
返回0说明等于
返回正数说明大于.*/
int test7() {
char str1[50], str2[50];
gets_s(str1);
gets_s(str2);
int cmp = strcmp(str1, str2);
if (cmp < 0)printf("str1 < str2");
else if (cmp > 0)printf("str1 > str2");
else printf("str1==str2");
return 0;
}
//3.strcpy 把字符数组2复制给字符数组1
int test8()
{
char str1[50], str2[50];
gets_s(str1);
gets_s(str2);
strcpy(str1, str2);
puts(str1);
return 0;
}
//4.strcat 把一个字符串接到另一个字符串后面
int test9()
{
char str1[50], str2[50];
gets_s(str1);
gets_s(str2);
strcat(str1, str2);
puts(str1);
return 0;
}
int main()
{
test9();
}
2.5.7 sscanf与sprintf
//sscanf and sprintf 可以理解为string + scanf ,和string + printf
/*
scanf("%d",&n);
printf("%d",&n);
等价于
scanf(screen,"%d",&n);
printf(screen,"%d",&n);
sscanf(str,"%d,&n");从左至右(把左边的str里的值 给了右边的n里面.)
sprintf(str,"%d,n");从右至左(把右边n里面的东西.给了左边str里面.)
*/
#include<cstdio>
int test1() {
int n;
char str[100] = "123";
sscanf(str, "%d", &n);
printf("%d", n);
return 0;
}
int test2() {
int n = 233;
char str[100];
sprintf(str, "%d",n);
printf("%s\n", str);
return 0;
}
/*
同时也可以进行复杂的格式输入和输出
char str[100] = "2048:3.14,hello",str2[100];
sscanf(str,"%d:%lf,%s", &n,&db, str2); //把str里面的东西叉开 然后分别存进右边的变量
*/
int main()
{
test1();
}
3
3.1 简单模拟 PAT B1001
/*#include<stdio.h>
int main()
{
int n;
int j = 0;
scanf("%d\n",&n);
while(n!=1)
{
j++;
if(n%2 == 0)//错误1.判断里面不应该是两个等号吗.
n=n/2;
else
n = (3*n+1)/2;//错误二,居然把乘号忘记了
}
printf(" 从n到1尽经历了 %d 次运算",j);
//莫名其妙输出不出来.. 好吧来瞅瞅正确答案
}
*/
#include<cstdio>
int main()
{
int n,step = 0;
printf("狗日的输出啊");
scanf("%d\n",&n);
while(n!=1)
{
if(n%2 == 0)
n = n/2;
else
n = (3*n+1)/2;
step++;
}
printf("出来了(出不来..) %d\n",step);
return 0;
}
3.1 简单模拟 PAT B1032
#include<cstdio>
const int maxn = 10010;
int school[maxn] = {0}; //记录每个学校的总分
int main(){
int n,schID,score;
scanf("%d",&n);
for(int i = 0;i<n;i++)
{
scanf("%d %d", &schID,&score); //学校的 ID 分数
school[schID] +=score; //学校schID的总分增加score
}
int k = 1,MAX = -1; //最高学校的ID和总分
for(int i = 1;i<=n;i++)
{
if(MAX < school[i]){
MAX = school[i];
k = i;
}
}
printf("%d %d\n",k,MAX);
return 0;
}
3.2 查找元素 codeup 1934找x
#include<cstdio>
int main(){
int a[100];
int n,x ,i;
scanf("%d",&n);
for(int i = 0;i<n;i++){
scanf("%d",&a[i]);//数组也要取地址符号啊.我以为都不用呢.
}
scanf("%d",&x);
for(i = 0;i<n;i++){
if(a[i]==x)
{
printf("%d\n",i);
// break;
}
// else if(i == n)
// printf("-1\n"); 在里面就不行.超出范围也不显示负一呢.
}
if(i == n)
printf("-1\n");
}
3.3图形输出
#include<cstdio>
int main(){
int row,col;
char c;
scanf("%d %c",&col, &c); //输入列数,和想要使用的字符
if(col%2==1)row = col/2+1;
else row = col/2;
//第一行
for(int i = 0;i<col;i++){
printf("%c",&c);
}
printf("\n");
//第2行到row-1行
for(int i = 2;i<row;i++){
printf("%c",c);
for(int j = 0;j<row;i++){
printf(" ");//col-2个空格.
}
printf("%c\n",c);
}
//第row行
for(int i = 0;i<col;i++){
printf("%c",c);
}
return 0;
}
3.4日期差值
#include<cstdio>
int month[13][2] = {//平年闰年天数
{0,0},{31,31},{28,19},{31,31},{30,30},{31,31},{30,30},{31,31},{31,31},{30,30},{31,31},{31,30},{31,31}
};
bool isLeap(int year) {
return(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
int main1() {
int time1, y1, m1, d1;
int time2, y2, m2, d2;
while (scanf_s("%d%d", &time1, &time2) != EOF) {//EOF是啥..
if (time1 > time2) {
int temp = time1;
time1 = time2;
time2 = temp;
}
y1 = time1 / 10000, m1 = time1 % 10000 / 100, d1 = time1 % 100;
y2 = time2 / 10000, m2 = time2 % 10000 / 100, d2 = time2 % 100;
int ans = 1;//记录结果.
//第一个日期没有达到第二个日期时候进行循环.
// 既!((y1 == y2)&&(m1 == m2)&&(d1 == d2))
while (y1 < y2 || m1 < m2 || d1 < d2) {
d1++;
if (d1 == month[m1][isLeap(y1)] + 1) {//满了当月天数
m1++;// 这嘎达抄错了..
d1 = 1;
}
if (m1 == 13) {//月份满12
y1++;
m1 = 1;
}
ans++;
}
printf("%d\n", ans);
}
return 0;
}
3.5进制转换
#include<cstdio>
//将p进制数x转换成十进制
/*
int y = 0,
int product = 1;//product在循环中会不断乘P得到,1,p,p^2,p^3;
int x;
while (x!= 0) {
y = y + (x % 10) * product;
x = x / 10;
product = product * P;
}
*/
//将十进制数y转换为Q进制数z 采用除基取余法
/*
int z[40], num = 0;
do {
z[num++] = y % Q;
y = y / Q;
} while (y != 0);//y等于零的时候也能执行一次存入z[0]=0
*/
int main(){
int a,b,d;
scanf("%d%d%d",&a,&b,&d);
int sum = a+b;
int ans[31],num = 0;
do{
ans[num++] = sum%d;
sum/=d;
}while(sum!=0);
for(int i = num-1;i>=0;i--){
printf("%d",ans[i]);
}
return 0;
}
3.6 PAT B1009
#include<cstdio>
#include<cstring>
int main(){
char str[90];
gets(str);
int len = strlen(str),r = 0,h = 0;//r为行,h为列
char ans[90][90];
for(int i = 0;i<len;i++){
if(str[i] != ' '){
ans[r][h++] = str[i];
}else{
ans[r][h] = '\0';
r++;
h = 0;
}
}
for(int i = r;i>=0;i--){ //倒着输出单词即可
printf("%s",ans[i]);
if(i>0) printf(" ");
}
return 0;
}
3.6字符串处理
#include<cstdio>
#include<cstring>
const int maxn = 256;
//判断字符串是否为回文串
bool judge(char str[]) {
int len = strlen(str);
for(int i = 0;i<len/2;i++){ //i枚举字符串的前一半
if(str[i] != str[len-1-i]){
return false;
}
}
return true;
}
int main(){
char str[maxn];
while(gets(str)){
bool flag = judge(str);
if(flag==true){
printf("YES\n");
}else{
printf("NO\n");
}
}
return 0;
}
4
4.1.1选择排序
//每次找到数组中最小元素.与最前面的交换..然后遍历范围缩减...
#include<stdio.h>
int A[] = { 3,1,4,5,2 };
int n = sizeof(A)/sizeof(int);
void selectSort(){
for (int i = 0; i <= n; i++)//进行n趟操作
{
int k = i; //中间变量
for (int j = i; j <= n; j++)//选出[i,n]的最小元素
{
if (A[j] < A[k])
{
k = j;
}
}
//交换两个值
int temp = A[i];
A[i] = A[k];
A[k] = temp;
}
}
int main()
{
selectSort();
for (int i = 0; i < n; i++)
{
printf("%d", A[i]);
}
return 0;
}
4.1.3 排序题 sort的应用 PAT A1025
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct Student{
char id[15]; //准考证号
int score; //分数
int location_number; //考场号
int local_rank; //考场内排名
}stu[30010];
bool cmp(Student a,Student b){
if(a.score != b.score) return a.score> b.score;// 先按分数从高到底排序
else return strcmp(a.id,b.id)<0 ;//分数相同按照准考证号从小到大排序
}
int main(){
int n,k,num = 0; //num为总考生数量
scanf("%d",&n); //考场数
for (int i = 1;i<=n ;i++)
{
scanf("%d",&k); //该考场内的人数
for(int j = 0;j<k;j++){
scanf("%s %d",stu[num].id,&stu[num].score);
stu[num].location_number = i; //考场号为i
num++; //总考生数量加1
}
sort(stu + num -k,stu+num,cmp); //将该考场的考生排序
stu[num-k].local_rank = 1; //该考场第一名的local rank计算为1;
for(int j = num-k+1;j<num;j++) { //剩余的考生.
if(stu[j].score==stu[j-1].score){ //如果与前一位考生同分.
//local_rank也相同.
stu[j].local_rank = stu[j-1].local_rank;
}else{
// local_rank为该考生前的人数.
stu[j].local_rank = j+1-(num-k);
}
}
}
printf("%d\n",num);//输出总考生数量.
sort(stu,stu+num,cmp);
int r = 1;
for(int i = 0;i<num;i++) {
if(i>0&&stu[i].score != stu[i-1].score){
r = i+1; //考生与上一个分数不同的时候让r跟新人数.
}
printf("%s ",stu[i].id);
printf("%d %d %d\n",r,stu[i].location_number,stu[i].local_rank);
}
return 0;
}
4.1.3插入排序
//将待插入的元素一个个插入到初始已经有序部分.遵循使插入后保持有序的原则
#include<stdio.h>
int A[] = { 3,1,4,5,2 };
int n = sizeof(A)/sizeof(int);
void insertSort(){
for (int i = 1; i <= n; i++)//进行n -1 趟排序
{
int temp = A[i] ,j = i; //中间变量 临时存放A[i] j从i 开始向前枚举
while(j > 0 && temp < A[j-1])//只要temp小于前一个元素
{
A[j] = A[j-1];
j --;
}
A[j] = temp;
}
}
int main()
{
insertSort();
for (int i = 0; i < n; i++)
{
printf(" %d", A[i]);
}
return 0;
}
4.4.1 简单贪心 PAT B1023组个最小数
#include<cstdio>
int main(){
int count[10];
for(int i = 0;i<10;i++){
scanf("%d",&count[i]);
}
for(int i = 1;i<10;i++){
if(count[i]>0){
printf("%d",i);
count[i]--;
break;
}
}
for(int i = 0;i<10;i++){
for(int j = 0;j<count[i];j++){
printf("%d",i);
}
}
return 0;
}
//2 2 0 0 0 3 0 0 1 0
4.4.1 简单贪心PAT B1020月饼
#include<cstdio>
#include<algorithm>
using namespace std;
struct mooncake{
double store;
double sell;
double price;
}cake[1010];
bool cmp(mooncake a,mooncake b){
return a.price>b.price;
}
int main()
{
int n;
double D;
scanf("%d%lf",&n,&D);
for(int i =0;i<n;i++){
scanf("%lf",&cake[i].store);
}
for(int i =0;i<n;i++){
scanf("%lf",&cake[i].sell);
cake[i].price = cake[i].sell/cake[i].store;
}
sort(cake,cake+n,cmp);//按单价从高到低排序.
double ans = 0;
for(int i = 0;i<n;i++){
if(cake[i].store<=D){
D-=cake[i].store;
ans+=cake[i].sell;
}else{
ans+=cake[i].price*D;
break;
}
}
printf("%.2f\n",ans);
return 0;
}
/*3 20
18 15 10 没弄出来..
75 72 45*/
4.4.2区间贪心
//给出N个开区间.(x,y)从中尽可能选择多的开区间.使得这些开区间两两没有交集.
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn1 = 110;
struct Inteval {
int x, y;//开区间左右端点.
}I[maxn1];
bool cmp(Inteval a, Inteval b) {
if (a.x != b.x) return a.x > b.x;
else return a.y < b.y;
}
int main() {
int n;
while (scanf("%d", &n), n != 0) {
for (int i = 0; i < n; i++) {
scanf("%d%d", &I[i].x,&I[i].y);
}
sort(I, I + n, cmp);//把区间排序.
//ans记录不相交区间个数,lastX记录上一个被选中区间的左端点.
int ans = 1, lastX = I[0].x;
for (int i = 1; i < n; i++) {
if (I[i].y <= lastX) {
lastX = I[i].x;
ans++;
}
}
printf("%d\n", ans);
}
return 0;
}
4.5.2二分查找
#include<cstdio>
//二分区间为左闭合右闭合的[left,right],传入的处置为[0,n-1]
int binarySearch(int A[],int left,int right,int x){
int mid;
while(left<=right){
mid = (left+right)/2;
if(A[mid]==x)return mid;
else if(A[mid]>x){
right = mid-1;
}else{
left = mid+1;
}
}
return -1;//查找失败返回-1
}
//1
int lower_bound(int A[],int left,int right,int x){
int mid;
while (left<right){
mid = (left+right)/2;
if(A[mid]>=x){
right = mid;
}else{
left = mid+1;
}
}
return left;
}
//2
int upper_bound(int A[],int left,int right,int x){
int mid;
while (left<right){
mid = (left+right)/2;
if(A[mid]>x){
right = mid;
}else{
left = mid+1;
}
}
return left;
}
//二分法拓展,
// 根号二的近似值
const double eps = 1e-5;//精度为10^-5
double f(double x){
return x*x;
}
double calSqrt(){
double left = 1,right = 2,mid;
while(right-left>eps){
mid = (left+right)/2;
if(f(mid)>2){ //mid
right = mid;//往左子区间[left mid]查找
}else{
left = mid;
}
}
return mid;
}
int main(){
const int n= 10;
int A[n] = {1,2,3,6,7,8,10,11,12,15};
printf("%d %d\n",binarySearch(A,0,n-1,6),binarySearch(A,0,n-1,9));
//第一小问.求序列第一个大于等于x元素的位置l
printf("%d\n",lower_bound(A,0,n-1,6));
//第二小问.求序列第一个大于x元素的位置l
printf("%d\n",upper_bound(A,0,n-1,6));
printf("%f\n",calSqrt());
return 0;
}
/*
寻找有序序列总第一个满足条件的元素位置,问题的固定模板.
二分区间为左闭右闭[left right]
初值必须能覆盖所有可能取值并且left 比最小取值小1
int solve(int left,int right){
int mid;
while (left<right){ //对于左闭右闭 left+1<right 意味着唯一位置.
mid = (left+right)/2;
if(条件成立){
right = mid;//往左子区间[left mid]查找
}else{
left = mid+1;
}
}
return left;
}
二分区间为左开右闭(left right]
int solve(int left,int right){
int mid;
while (left+1<right){ //对于左开右闭(left right] left+1<right 意味着唯一位置.
mid = (left+right)/2;
if(条件成立){
right = mid;//往左子区间[left mid]查找
}else{
left = mid;
}
}
return right;
}
*/
4.5.3快速幂
//给定三个正数.a,b,m(a<10^6,b<10^6,1<m<10^9) 求a^b%m.
//时间复杂度O(b)
typedef long long LL;
LLpow (LL a,LL b,LL m){
LL ans = 1;
for(int i = 0;i<b;i++){
ans = ans*a%m;
}
return ans;
}
//时间复杂度O(logb)ku快速幂 的递归写法.
LL binaryPow(LL a,LL b,LL m){
if(b==0) return 1;
//b为奇数转换为b-1
if(b%2==1) return a*binaryPow(a,b-1,m)%m;
//b为偶数转换为 b/2
else{
LL mul = binaryPow(a,b/2,m);
return mul*mul%m;
}
}
//迭代写法
LL binaryPow(LL a,LL b,LL m){
LL ans = 1;
while(b>1){
if(b&1){ //如果二进制末尾为1.
ans = ans*a%m;//令ans累计上a
}
a = a*a%m;
b>>1;//将b的二进制右移动一位.
}
return ans;
}
4.6.1two point
//在一个有序数组中找到两个和为m的一组数
while(i < j)
{
if(a[i] + a[j] == m)
{
printf(" %d %d\n",i ,j);
i ++;
j --;
}else if(a[i] + a[j] < m) i ++;
else j --;
}
//把两个递增的序列合并成一个递增的序列
int merge(int A[],int B[],int C[],int n,int m){//n,m是A,B的数组大小.
int i = 0,j = 0,index = 0; //i指向A[0] j指向B[0]
while (i<n&&j<m){
if(A[i]<=B[j]){ //如果A[i]<=B[j] 将A[i]加入序列C
C[index++] = A[i++];
}else{
C[index++] = B[j++]; //如果A[i]>B[j] 将B[j]加入序列C
}
}
while (i<n) C[index++] = A[i++];
while (j<m) C[index++] = B[j++]; //把剩下的加上去.
return index;
}
//使用两个下标i ,j对序列进行扫描,以较低的复杂度解决问题(t p 是一种思想)
4.6.2归并排序
//将序列两两分组.分成N/2个组(比如;有七个数,先分四个组.再分两个组..)组内单独排序.直到剩下一个组.
//时间复杂度nlogn
#include<stdio.h>
//#include<math.h>
#include<algorithm>
using namespace std;algorithm 这俩东西再用min 的时候配套了.
int A[] = { 66, 12, 33, 57, 27, 18 , 15 };
int n = sizeof(A)/sizeof(int);
const int maxn = 100;
//将数组A 的两个连续的区间合并成有序区间..
int merge(int A[],int L1,int R1,int L2,int R2){
int i = L1,j = L2; //i指向A[L1] j指向A[L2]
int temp[maxn],index = 0; //temp临时存放合并后的数组.index为其下标
while (i <= R1 && j <= R2){
if(A[i] <= A[j]){ //如果A[i]<=A[j] 将A[i]加入序列temp
temp[index++] = A[i++];
}else{
temp[index++] = A[j++]; //如果A[i]>A[j] 将A[j]加入序列temp
}
}
while (i <= R1) temp[index++] = A[i++];
while (j <= R2) temp[index++] = A[j++]; //把剩下的加上去.
for(int i = 0;i < index;i++)
A[L1+i] = temp[i];//将合并后的数组返回数组A
}
//递归实现版本
//将array数组当前区间{left,right}进行归并排序.
void mergeSort(int A[],int left,int right){
if(left<right){
int mid = (left+right)/2;
mergeSort(A,left,mid);
mergeSort(A,mid+1,right);
merge(A,left,mid,mid+1,right);
}
}
// 非递归实现
void mergeSort1(int A[])
{
//sept 为组内元素个数,step/2为左子区间元素个数,注意等号可以不取.
for(int step = 2;step / 2 <= n;step *= 2)
{
//每step个元素一组,组内前step/2和后step/2个元素进行合并.
for(int i = 1; i <= n; i += step)
{//对每一组
int mid = i + step / 2 - 1; //
if(mid + 1 <= n)
merge(A, i, mid, mid+1 , min (i + step - 1, n));
}
}
}
//如果题目值要求给出归并排序的每一趟结束时的序列,那可以用sort函数来代替merge函数
//题目只要求给出每一趟结束的序列.那就不使用merge.可以直接sort
void mergeSort2(int A[]){
//sept 为组内元素个数,step/2为左子区间元素个数,注意等号可以不取.
for(int step = 2;step/2<=n;step*=2){
//每step个元素一组,组内前step/2和后step/2个元素进行合并.
for(int i = 1;i <= n;i += step){//对每一组
sort(A+i,A + min(i + step,n + 1));
}
//此处可以输出归并排序的某一趟结束的序列.
for (int i = 0; i < n; i++)
{
printf(" %d", A[i]);
}
printf("\n");
}
}
int main()
{
// mergeSort(A,0,n - 1);
for (int i = 0; i < n; i++)
{
printf(" %d", A[i]);
}
printf("\n");
//mergeSort1(A); //输出失败.sorry
for (int i = 0; i < n; i++)
{
printf(" %d", A[i]);
}
printf("\n");
return 0;
}
4.6.3快速排序
//1 A[left]是主元
int Partition(int A[],int left,int right){
int temp = A[left]; //将A[left]存入临时遍历temp中
while(left<right){
while(left<right&&A[right]>temp) right--;//反复左移right
A[left] = A[right];
while(left<right&&A[left]<=temp) left++;//反复右移left
A[right] = A[left];
}
A[left] = temp;
return left; //返回相遇的下标
}
//快速排序
void quickSort(int A[],int left,int right) {
if(left<right) {//当前区间长度不超过1
//将[left,right]按照A[left]一分为二
int pos = Partition(A,left,right);
quickSort(A,left,pos-1);
quickSort(A,pos+1,right);
}
}
//如何生成随机数.
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<math.h>
int main(){
srand((unsigned)time(NULL));//必须有.
for(int i = 0;i<10;i++){
printf("%d ",rand()%2);//[0,1]
}
printf("\n");
for(int i;i<10;i++){
printf("%d ",rand()%5+3);//[3,7] (b-a+1)+a
}
printf("\n");
for(int i = 0;i<10;i++){//[10000,60000] RAND_MAX常量 32767
//(round(1.0*rand()/RAND_MAX*50000+10000))
printf("%d ",(int)(round(1.0*rand()/RAND_MAX*50000+10000)));
}
return 0;
}
//随机选取主元,对区间[left,right]进行划分.
int randPartition(int A[],int left,int right){
//生成[left,right]内的随机数P
int p = (round(1.0*rand()/RAND_MAX*(right-left)+left)) ;
swap(A[p],A[left]);
//以下为原先partition函数的划分过程.
int temp = A[left]; //将A[left]存入临时遍历temp中
while(left<right){
while(left<right&&A[right]>temp) right--;//反复左移right
A[left] = A[right];
while(left<right&&A[left]<=temp) left++;//反复右移left
A[right] = A[left];
}
A[left] = temp;
return left; //返回相遇的下标
}
5
5.6大整数运算
#include<stdio.h> //标准输入输出
#include<string.h> //C语言字符数组字符串
struct bign{
int d[1000];
int len;
bign(){ //构造函数
memset(d,0,sizeof(d));//对数组全部初始化赋值0
len = 0;
}
};
//把输入的字符串调整成大数的约定格式,(低位存在低位)需要逆置
bign change(char str[]){
bign a;
a.len = strlen(str);//bign的长度就是字符串的长度
for(int i = 0;i<a.len;i++){
a.d[i] = str[a.len-i-1]-'0';//逆着赋值
}
return a;
}
//比较两个bign变量的大小
//int compare(bign a,bign b){
// if(a.len>b.len)return 1;
// else if(a.len<b.len)return -1;
// else {
// int i = a.len-1;//放在里面显示为定义i
// for(;i>=0;i--);//从高位向低位比较
// {
// if(a.d[i]>b.d[i]) return 1;
// else if(a.d[i]<b.d[i]) return -1;
// }
// return 0;//相等
// }
//}
//大整数的四则运算.
//高精度加法.
bign add(bign a,bign b){
bign c;
int carry = 0;//carry是进位
for(int i = 0;i<a.len||i<b.len;i++){// 以较长的为界限
int temp = a.d[i] + b.d[i] +carry;//对应位于进位相加
c.d[c.len++] = temp%10;
carry = temp/10; //十位数为新的进位.
}
if(carry != 0){
c.d[c.len++] = carry;
}
return c;
}
//高精度减法
bign sub(bign a,bign b){
bign c;
for(int i = 0;i<a.len||i<b.len;i++){
if(a.d[i]<b.d[i]){
a.d[i+1]--;//向高位借位;
a.d[i]+=10;//当前为加10
}
c.d[c.len++] = a.d[i]-b.d[i];
}
while(c.len-1>=1&&c.d[c.len-1]==0){
c.len--;
}
return c;
}
void print(bign a){
for(int i = a.len-1;i>=0;i++){
printf("%d",a.d[i]);
}
}
//炸了.未完单徐
int main()
{
char str1[1000],str2[1000];
scanf("%s%s",str1,str2);
bign a = change(str1);
bign b = change(str2);
print(add(a,b));
return 0; //返回0,不然pat报错
}
5.8.2组合数的计算
//组合数计算C(n,m)n个数中取出m个组合
//递归代码 C(n,m)=C(n-1,m)+C(n-1,m-1)
long long C(long long n,long long m){
if(m==0||m==n) return 1;
return C(n-1,m)+C(n-1,m-1);
}
//减少重复计算的递归代码
long long res[67][67] = {0};
long long C(long long n,long long m){
if(m==0||m==n) return 1;
if(res[n][m] != 0) return res[n][m];
return res[n][m] = C(n-1,m)+C(n-1,m-1);
}
//把整张表都计算出来的递推代码
const int n = 60;
void calC(){
for(int i = 0;i<=n;i++){
res[i][0] = res[i][i] = 1;
}
for(int i = 2;i<=n;i++){
for(int j = 0;j<=i/2;j++){
res[i][j] = res[i-1][j]+res[i-1][j-1];//递推计算c(i,j)
res[i][j-1] = res[i][j];//C(i,i-j) = C(i,j)
}
}
}
//如何计算 C(n,m)%p
int res[1010][1010] = {0};
int C(int n,int m,int p){
if(m==0||m==n) return 1;
if(res[n][m] != 0) return res[n][m];
return res[n][m] = C(n-1,m)+C(n-1,m-1)%p;
}
//把整张表都计算出来的递推代码
const int n = 60;
void calC(){
for(int i = 0;i<=n;i++){
res[i][0] = res[i][i] = 1;
}
for(int i = 2;i<=n;i++){
for(int j = 0;j<=i/2;j++){
res[i][j] = (res[i-1][j]+res[i-1][j-1])%p;//递推计算c(i,j)
res[i][j-1] = res[i][j];//C(i,i-j) = C(i,j)
}
}
}
6
6.1vector常见用法
#include<vector>
#include<cstdio>
using namespace std;
//vector<typename> name;
//vector<typename> Arrayname[arraySize];
//迭代器(iterator)可以理解成类似指针的东西.其定义是
//vector<typename>::iterator it;
void solve(){
vector<int> vi;
for(int i = 1;i<=5;i++){
vi.push_back(i);
}
//vi.begin();为取vi的首元素地址
vector<int>::iterator it = vi.begin();
for(int i = 0;i<5;i++){
printf("%d ",*(it+i));
}
}
//从这里可以看出来vi[i]和*(vi.begin()+i)是等价的
//另一种遍历方法
void solve1(){
vector<int> vi;
for(int i = 1;i<=5;i++){
vi.push_back(i);//在vector后面添加一个元素.
//vi.pop_back()可以删除vi末尾的元素.
//size()用来获得vector中元素的个数.返回的是无符号类型的.但是可以用%d输出
//clear()用来清空vector数组中所有元素.
//insert(it,x)用来向vector的任意迭代器it处插入一个元素x.
//例子:vi.insert(vi.begin()+2,-1)将-1插入到2的位置.
//erase():删除单个元素,删除一个区间的元素.
//erase(it)/erase(first,last);
}
//迭代器支持自加自减操作.
for(vector<int>::iterator it = vi.begin();it != vi.end();it++){
printf("%d ",*it);
}
}
int main(){
solve();
printf("类似下标和指针的输出\n");
solve1();
printf("展现it可以自增\n");
return 0;
}
6.2set常见用法
#include<cstdio>
#include<set> //内部有序,不含重复的容器.
using namespace std;
void solve(){
set<int>st1;
st1.insert(100);
st1.insert(200);
st1.insert(100);
st1.insert(300);
st1.insert(400);
st1.erase(st1.find(100));//1 st.erase(it)
st1.erase(400);//2 st.erase(value);
//set<int>::iterator it = st.find(30);
//st.erase(it,st.end());//删除30到结尾之间的元素
//size()获得set内元素个数
//clear()清空set中的所有元素.
for(set<int>::iterator it1 = st1.begin();it1 != st1.end();it1++){
printf("%d\n",*it1);
}
}
int main(){
set<int>st;
for(int i = 1;i<=3;i++){
st.insert(i);//可以将i插入到set容器中.
}
//set只能通过迭代器访问.*it就是set中的值了
set<int>::iterator it = st.find(2); //find(value)可以返回对应值为value的迭代器.
printf("%d\n",*it);
//以上两句也可以写成,,但是dev上跑不起来
//printf(%d\n",*(st.find(2));
solve();
return 0;
}
6.3string常见用法
#include<stdio.h>
#include<iostream>
#include<string>
using namespace std;
//读入和读出.
void solve(){
//length()和size,获得存放的字符数
//insert(pos,string)在pos号位置插入字符串string
//insert(it,it2,it3)在原字符串的it位置开始插入,it2和it3是首尾迭代器.
//erase(it)删除单个元素.
//erase(first,last)删除区间.
//erase(pos,length)从pos开始删除length个元素.
//clear()用以清空string 中的数据.
//substr(pos,len) 返回 从pos位置开始,长度为len的子串.
//string::npos 是一个常数.其本身的值为-1 unsigned_int类型.
//是find 函数失配时候的返回值.
//find(str2) 当str2是str1的子串的时候返回其在str1中第一次出现的位置.
//find(str2,pos)从str1的pos位置开始查找与上面相同.
//str1.find(str2,7)
//replace(pos,len,str2) 从pos位置开始长度为len的子串替换为str2.
//replace(it1,it2,str2) 把str2迭代器[it1,it2)范围的子串替换成为str2.
//两个string可以通过=..来比较大小.
}
int main(){
string str = "abcd";
for(int i = 0;i<str.length();i++){
printf("%c",str[i]);
}
printf("\n");
//如果要读入和输入整个字符串则只能用cin,cout
string s;
cin>>s;
cout<<s;
//可以用c_str将string 转换成字符数组就可以用printf输出了
printf("%s字符数组的输出\n",str.c_str());
//insert函数和erase函数要用迭代器访问So string迭代器不用写类型了.
for(string::iterator it = str.begin();it!=str.end();it++){
printf("%c迭代器访问",*it);
}
//字符串加法
string str3;
str3 = str+s;
str += s; //把s接上去str
cout<<"\n字符串加法\n"<<str3<<endl;
cout<<str <<endl;
solve();
return 0;
}
6.4map常用用法
//map翻译为映射.map可以将任何基本类型.映射到基本类型包括 STL容器
//map<typename1,typename2> mp;
//映射前类型key (键) 映射后类型value (值)
//map会以键从小到大排序.
//map可以通过下标和迭代器来访问.
#include<cstdio>
#include<map>
using namespace std;
int main(){
map<char,int> mp;
mp['m'] = 20;
mp['r'] = 30;
mp['a'] = 40;
//迭代器访问 --失败
// map<char,int>::iterator it = mp.begin(); //不定义在for外面就报错.难搞啊.
// for(it = mp.begin();it != mp.end();it++);
// {
// printf("%c %d\n",it->first,it->second);
// }
//下标访问
printf("下标访问:%d\n",mp['r']);
// find 返回关键值为key的映射迭代器
map<char,int>::iterator it = mp.find('m');
printf("find返回关键字:%c %d\n",it->first,it->second);
//erase(it) 删除单个
//erase(key) 删除的映射的键.
//erase(first,last)
//size clear
return 0;
}
6.5queue常见用法
#include<stdio.h>
#include<queue>
using namespace std;
int main(){
queue<int>q;
if(q.empty()==true){
printf("Empty\n");
}else{
printf("Not Empty%d\n",q.size());
}
for(int i = 1;i<=5;i++){
q.push(i);//
}
for(int i = 1;i<=3;i++){
q.pop();//首元素出队 空参哦
}
if(q.empty()==true){
printf("Empty\n");
}else{
printf("Not Empty : %d\n",q.size());
}
// 使用front和pop前必须判断是不是为空
printf("%d\n",q.front());
printf("%d\n",q.back());
return 0;
}
6.6 priority_queue常见用法
//priority_queue 没有front()和back() 只能通过top来访问队首部元素.
//基本数据类型优先级设置
//priority_queue <int,vector<int>,less<int> > q; 表示数字越大优先级越大.greater<int>表示数字越小优先级越大.
#include<stdio.h>
#include<iostream>
#include<string>
#include<queue>
using namespace std;
//结构体的优先级设置
struct fruit{
string name;
int price;
//重载小于号运算符.
friend bool operator<(fruit f1,fruit f2){
return f1.price > f2.price;//价格低的优先. 但是在sort排序cmp中却正好相反.
}
}f1,f2,f3;//创造对象.(错)
struct cmp{//挺奇特
bool operator() (fruit f1,fruit f2){
return f1.price> f2.price;
}
};
int main(){
priority_queue<int,vector<int>,greater<int> >q;
q.push(3);
q.push(4);
q.push(1);
//使用top前必须使用empty判断队列是否为空
printf("%d\n",q.top());
//结构体的优先级设置
priority_queue<fruit> p;
f1.name = "桃子";
f1.price = 3;
f2.name = "梨子";
f2.price = 4;
f3.name = "苹果";
f3.price = 1;
p.push(f1);
p.push(f2);
p.push(f3);
cout<<p.top().name<<" "<<p.top().price<<endl;
//cmp类型的排序
priority_queue<fruit,vector<fruit>,cmp> k;
f1.name = "桃子";
f1.price = 3;
f2.name = "梨子";
f2.price = 4;
f3.name = "苹果";
f3.price = 1;
k.push(f1);
k.push(f2);
k.push(f3);
cout<<k.top().name<<" "<<k.top().price<<endl;
return 0;
}
6.8 pair常见用法
// pair 可以将两个元素绑在一起作为一个合成元素.类似下面的东西
/*struct{
typeName1 first;
typeName2 second;
}; */
//可以比较大小,先比first first相等再比second;
//常见用途
1.可以代替二元结构体的和其构造函数
2.作为map的键值进行插入.
mp.insert(make_pair("嘿嘿",5"));
mp.insert(pair<string,int >("heihei",555));
#include<iostream>
#include<utility>//可以偷懒用map代替
#include<string>
using namespace std;
int main(){
pair<string ,int> p;
p.first = "haha";
p.second = 5;
cout<<p.first<<" "<<p.second<<endl;
//临时构造一个pair
p = make_pair("xixi",55);
cout<<p.first<<" "<<p.second<<endl;
p = pair<string,int >("heihei",555);
cout<<p.first<<" "<<p.second<<endl;
return 0;
}
6.9 algorithm常见用法
//max(),min(),abs()参数必须是两个.可以是浮点数.
//但是abs()必须是整数.浮点数绝对值需要用math下的fabs
//swap(),reverse(it,it2), 可以将数组指针[it,it2)之
//间的元素或者容器的迭代器在[it,it2)范围内进行反转
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
bool cmp(int a,int b){
return a>b;
}
int main(){
int a[10] = {1,2,3,4};
//a[0]~a[2]之间的序列需要求解next_permutation
do{
printf("%d%d%d%d\n",a[0],a[1],a[2],a[3]);
} while(next_permutation(a,a+4));//有几个数要全排列就加几...尾元素的下一个地址.
fill(a,a+4,233 );
for(int i = 0;i<4;i++){
printf("%d ",a[i]);
}
//容器的排序
vector<int> vi;
vi.push_back(3);
vi.push_back(1);
vi.push_back(2);
sort(vi.begin(),vi.end(),cmp);
int i = 0;
for(i;i<3;i++){
printf("%d ",vi[i]);
}
// 比较函数cmp 这个是配合着sort函数使用的作为sort函数的第三了参数来左右排序的规则.
bool cmp(string str1,string str2)
{
return str1.length() < str2.length();//按照字符串长度有小到大. 大于号就是由大到小.记不住就都试试.
}
/*
lower_bound(first,last,val)//在范围内寻找第一个值大于等于val的元素的位置.是数组就返回该位置的指针,容器就返回迭代器
upper_bound(first,last,val)//大于val的值.
*/
return 0;
}
7.3.3链表的基本操作
#include<stdio.h>
#include<stdlib.h>
struct node {
int data;
node *next;
};
//创建链表
node *create(int Array[])
{
node *p, *pre, *head; //pre保存当前结点的前驱 ,head 为头结点
head = new node; //创建头结点
head -> next = NULL; //头结点不需要数据域。指针域初始化为NULL
pre = head;//记录pre为head
for(int i = 0; i < 5; i ++)
{
p = new node;
p -> data = Array[i];
p -> next = NULL;
pre -> next = p;
pre = p;
}
return head;
}
int main()
{
int Array[] = {5 , 3, 6, 1, 2};
node *L = create(Array); //创建链表,返回的头指针head赋值给L
node *k = L -> next;
while(k != NULL)
{
printf("%d", k ->data);
k = k->next;
}
return 0;
}
8.1深度优先搜索
/*
深度优先搜索是一种枚举所有完整路径以遍历所有情况的搜索方法.
有n个物品.装进背包.背包容量V 物品的重量和价值为w[i] c[i] 怎样装可以令价值最大化
输入:
5 8
3 5 1 2 2
4 5 2 1 3
*/
#include<cstdio>
const int maxn = 30;
int n, V, maxValue = 0; //物品数量,背包容量,最大价值
int w[maxn], c[maxn]; //每个重量和价值
//index 为当前处理物品的编号
//sumW和sunC分别为总重量和总价值
void DFS1(int index, int sumW, int sumC)
{
if(index == n)// 完成已经对n件物品的选择(死胡同)
{
if(sumW <= V && sumC > maxValue)
maxValue = sumC;
return;
}
//岔道口
DFS1(index + 1,sumW, sumC); //不选第index件物品
DFS1(index + 1, sumW + w[index], sumC + c[index]);//选第index件物品
}
//时间复杂度为O(2^n) 可以优化
//剪枝优化
void DFS(int index, int sumW, int sumC)
{
if(index == n)// 完成已经对n件物品的选择(死胡同)
return;
DFS(index + 1,sumW, sumC); //不选第index件物品
//岔道口
//只有加入第index件物品后为超过容量V,才能继续
if(sumW + w[index] <= V)
if(sumC + c[index] > maxValue)
maxValue = sumC + c[index];
DFS(index + 1, sumW + w[index], sumC + c[index]);//选第index件物品
}
int main()
{
scanf("%d%d", &n, &V);
for(int i = 0;i < n; i++)
scanf("%d", &w[i]); //每件物品的重量
for(int i = 0; i < n;i ++)
scanf("%d", &c[i]); //每件物品的价值
DFS1( 0, 0, 0); //初始时第 0 件物品,当总重量和总价值均为0
printf("%d\n", maxValue);
return 0;
}
8.2广度优先搜索
//广度优先搜索.把每个分叉的第一个点先遍历然后每次深入一个点从左到右增加.
/*需要辅助队列.
迷宫问题. 走到终点需要的最小步数量.
5 5
.....
.*.*.
.*S*.
.***.
...T*
2 2 4 3
没有成功 每次都返回负一
*/
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 100;
struct node {
int x, y; //location
int step; // min step
}S, T, Node; //s为起点,T为终点,Node为临时结点.
int n, m; //n 为行 ,m为列
char maze[maxn][maxn]; //迷宫信息
bool inq[maxn][maxn]; //记录位置(x,y)是否进入过队
int X[4] = {0, 0, 1, -1}; //增量数组
int Y[4] = {1, -1, 0, 0};
//检测位置(x,y)是否有效
bool test(int x,int y){
if(x >= n || x < 0 || y >= m || y < 0) return false; //超越边界
if(maze[x][y] == '*') return false; //墙壁
if(inq[x][y] = true) return false; //已经入队过
return true;
}
int BFS()
{
queue<node> q;
q.push(S);
while(!q.empty())
{
node top = q.front();
q.pop();
if(top.x == T.x && top.y == T.y)
return top.step;//终点直接返回最少步数
for(int i = 0;i < 4;i ++)
{
int newX = top.x + X[i];
int newY = top.y + Y[i];
if(test(newX,newY))
{
Node.x = newX,Node.y = newY;
Node.step = top.step + 1;
q.push(Node);
inq[newX][newY] = true;
}
}
}
return -1;//无法到达终点T是返回-1
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 0;i < n;i++)
{
getchar();//
for(int j = 0;j < m;j ++)
maze[i][j] = getchar();
maze[i][m + 1] = '\0';
}
scanf("%d%d%d%d", &S.x, &S.y, &T.x, &T.y);
S.step = 0;
printf("%d\n",BFS());
return 0;
}