算法2022年3月20日

2022年3月20日

模拟题

  • %g输出
  • case 4:的情况
  • double 类型 输入%lf 输出%f
//2022年3月20日09:38:29~ 
#include<cstdio> 
/*
某百货公司为了促销,采用购物打折的优惠方法,每位顾客一次购物:
在1000元以上者,按9.5折优惠;
在2000以上者,按9折优惠;
在3000以上者,按8.5折优惠;
在5000以上者,按8折优惠;
编写程序,购物款数,计算并输出优惠价。
*/ 
int main(){
	
	double cost;
	while(scanf("%lf", &cost) != EOF){
		int t = cost/1000;
		double discount = 1;
		switch(t){
			case 0:
				discount = 1;
				break;
			case 1:
				discount = 0.95;
				break;
			case 2:
				discount = 0.9;
				break;
			case 3:
			case 4:
				discount = 0.85;
				break;
			default:
				discount = 0.8;
				break;
		}
		printf("discount=%g,pay=%g\n", discount, discount*cost);
	} 
	return 0;
} 

模拟题1133

  • (1+n)*n/2
#include <cstdio>
int main(){
	int n;
	scanf("%d", &n);
	printf("%d", (1+n)*n/2);
	return 0;
}

模拟题1043

  • 需要一个中间变量生成aaa…
#include<cstdio>
/*
求Sn=a+aa+aaa+…+aa…aaa(有n个a)之值,
其中a是一个数字。 
例如:2+22+222+2222+22222(n=5),
*/
int main(){
	
	int a, n;
	scanf("%d%d", &a, &n);
	int sum = 0;
	int t = 0;
	for(int i = 0; i < n; i++){
		t = t*10 + a;
		sum += t;
		//printf("%d ", sum);
	}
	printf("%d", sum);
	return 0;
}

模拟题1040

  • 那个*2 *4没有注意到
#include<cstdio>
/*
企业发放的奖金根据利润提成。
利润低于或等于100000元的,奖金可提10%;
100000<I≤200000)时,低于100000元的部分按10%提成,高于100000元的部分,可提成 7.5%;
200000<I≤400000时,高于200000元的部分按5%提成;
400000<I≤600000元时,高于400000元的部分按3%提成;
600000<I≤1000000时,高于600000元的部分按1.5%提成;
I>1000000时,超过1000000元的部分按1%提成。
从键盘输入当月利润I,求应发奖金总数。
*/
int main(){
	int profit;
	scanf("%d", &profit);
	int bonus;
	if(profit <= 100000){
		bonus = profit*0.1;
	}else if(profit <= 200000){
		bonus = 10000+(profit-100000)*0.075; 
	}else if(profit <= 400000){
		bonus = 100000*(0.1+0.075) + (profit-200000)*0.05;
	}else if(profit <= 600000){
		bonus = 100000*(0.1+0.075+0.05*2)+(profit-400000)*0.03;
	}else if(profit <= 1000000){
		bonus = 100000*(0.1+0.075+0.05*2+0.03*2)+(profit-600000)*0.015;
	}else{
		bonus = 100000*(0.1+0.075+0.05*2+0.03*2+0.015*4)+(profit-1000000)*0.01;
	}
	printf("%d", bonus);
	return 0;
} 

进制转换

模拟题(反序数三种方法)

  • 老老实实反序,itoa()函数不在标准库中,跨平台可能出问题
  • sscanf(str, “%d”, &n);//把字符数组str中的内容,以%d的形式,输入到n中
  • sfrintf(str, “%d”, n);//把n以%d的形式输出到str中
  • scanf(screen, “%d”, &n);//从左到右,把屏幕上的字符串—>其他类型
  • printf(screeen, “%d”, n);//从右到左,把其他类型---->屏幕上的字符串
  • / 是去掉最后n位,%是取出来最后n位
  • 把每个临时变量的含义搞清楚,需要一个保存答案的,还有一个接收最后一位的。
#include<cstdio>
using namespace std;
int main(){
	int n;

	while(scanf("%d", &n) != EOF){
		//转换
		// /去掉后n位,%取后n位
		int ans = 0;
		int t = 0;
		while(n!=0){
			t = n % 10;//取最后一位 
			ans = ans * 10 + t;//计算
			n /= 10;//更新 
		} 
		printf("%d\n", ans); 
	} 
	return 0;
}

不老实的写,巩固一下string 和 char[]的转换

  • string 类型只能cout输出 cin输入
  • string str—>const char* cstr 使用string 的c_str()函数,返回的结果是只读的
  • 可以用 char cs[100]; strcpy(cs, str.c_str()),将返回的结果保存在字符数组变量中,strcpy(),strlen(),strcat()等函数都是操作的char[]数组而不是string,在‘’cstring‘’中
  • 使用sscanf(cstr, “%d”, &n),可以将char cstr[100]---->int n;
  • 使用sprintf(cstr, “%d”, n);可将int n---->char cstr[100]
  • 使用reverse(str.begin(), str.end());可以将string str,中的内容反转
  • reverse(cstr, cstr+n),也可以反转
#include<cstdio>
#include<string>
#include<algorithm>
using namespace std;
int main(){
	
	int n;
	char cs[11];
	string str;
	const char *cstr;//内容不可变更 
	while(scanf("%d", &n) != EOF){
		//将n--->cs
		sprintf(cs, "%d", n);
		
		//将cs--->str
		str = cs;
		
		//reverse()   str
		reverse(str.begin(), str.end());//迭代器、地址 
		//将str--->cstr
		cstr = str.c_str(); 
		//将cstr-->n
		sscanf(cstr, "%d", &n);
		printf("%d\n", n); 
	}
	return 0;
}

嘤,改进了一下,

  • 使用‘’cstring‘’中的strlen(cstr) 函数和 reverse(cstr, cstr+n),不使用迭代器作为参数,使用地址
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int main(){
	int n;
	 
	while(scanf("%d", &n) != EOF){
		char cstr[100];
		//将 n--->cstr
		sprintf(cstr, "%d", n);
		//将cstr反转
		reverse(cstr, cstr+strlen(cstr));
		//将cstr--->n
		sscanf(cstr, "%d", &n);
		
		//输出
		printf("%d\n", n); 
	}
	return 0;
} 

反序数变形

  • 清华大学机试题
  • 用上面自创的方法求哈哈哈三行代码
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
//1454
/*
设N是一个四位数,它的9倍恰好是其反序数
(例如:1234的反序数是4321)
求N的值
*/ 

int trans(int n){
	//求n的逆序数
	char s[5];
	//int -->  char* 
	sprintf(s, "%d", n);
	//reverse
	reverse(s, s+strlen(s));
	//char* --> int
	sscanf(s, "%d", &n);
	return n;
	 
}
int main(){
	for(int i = 1000; i <= 1112; i++){
		if(i*9 == trans(i)){
			printf("%d", i);
		}
	}
	return 0;
}

进制转换(1259)

  • 北京大学机试题
  • 其他进制转为十进制,从高位开始更方便一点,符合计算机的思维
  • 还是区分是字母的时候要-‘A’+10
#include<cstdio>
#include<cstring>
/*
写出一个程序,接受一个十六进制的数值字符串,
输出该数值的十进制字符串
(注意可能存在的一个测试用例里的多组数据)。
*/
int main(){
	char sn[30];
	while(scanf("%s", sn) != EOF){
		//16进制转10进制,从高位开始 
		int ans = 0;
		for(int i = 2; i < strlen(sn); i++){
			if(sn[i] <= '9'){
				ans = ans*16 + (sn[i]-'0');
			}else{
				ans = ans*16 + (sn[i]-'A'+10);
			}
		} 
		printf("%d\n", ans);
	}
	
	return 0;
}

大整数的十进制和二进制转换

  • 清华大学上机题
  • 也可以改为通用的进制转换
  • 注意得到的返回结果是小端存储的,即低位在string的低位
  • 其实就是模拟整数的除法过程
/*
 对于一个十进制数A,将A转换为二进制数,
 然后按位逆序排列,再转换为十进制数B,
 我们称B为A的二进制逆序数。    
  例如对于十进制数173,它的二进制形式为10101101,逆序排列得到10110101,其十进制数为181,181即为173的二进制逆序数。
*/
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
string change(string num, int oldbase, int newbase){
	string ans = "";
	//开始进制转换,从高位开始
	int len = num.length();
	for(int i = 0; i < len;){//i是num的最高位的下标 
		//除基取余法
		int r = 0;//余数,保存最后的余数  
		for(int j = i; j < len; j++){
			int rtemp = (r*oldbase+num[j]-'0') % newbase;//中间的余数 
			num[j] = (r*oldbase+num[j]-'0') / newbase+'0';
			r = rtemp;
		}	
		ans += r+'0';
		while(num[i] == '0')i++;
	} 
	return ans;
}
int main(){
	string num;
	while(cin>>num){
		string bnum = change(num, 10, 2);//把10进制转化为2进制 
		//reverse(bnum.begin(), bnum.end());
		string ans = change(bnum, 2, 10);//把2进制转化为10进制
		reverse(ans.begin(), ans.end());
		cout<<ans<<endl; 
	}
	return 0;
}

模拟题1178(两种方法)

方法一:使用大整数的除法只针对十进制转2进制时大整数/2

#include<cstdio>
#include<cstring>

int main(){
	char snum[33];//十进制大整数
	while(scanf("%s", snum) != EOF){
		char ans[100];//保存二进制结果 
		int index = 0;
		int num[33];
		for(int i = 0; i < strlen(snum); i++){
			num[i] = snum[i]-'0';
		}
		int len = strlen(snum);
		
		int i = 0;//num数组第一个非0所在的下标
		
		while(i < len){
			ans[index++] = num[len-1] % 2 + '0';
			
			//大整数除法
			int carry = 0;
			for(int j = i; j < len; j++){
				int tem = num[j];
				num[j] = (carry + num[j])/ 2;
				if(tem & 1){
					carry = 10;
				} else{
					carry = 0;
				}
			} 
			if(num[i] == 0) i++;
		} 
		//输出
		for(int i = index-1; i >= 0; i--){
			printf("%c", ans[i]);
		} 
		printf("\n");
	} 
	return 0;
}

方法二:使用上一道的模拟方法更通用一点

  • string ans='“”;初始值
  • 更新num[j] 时+‘0’
  • 更新ans时+‘0’
  • 去掉最高位while()
  • 最后得到的结果是小端存放,转为大端(低下标存放最高位)
//1178十进制大整数转2进制第二版
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
//多组数据,每行为一个长度不超过30位的十进制非负整数。
//(注意是10进制数字的个数可能有30个,而非30bits的整数)
string change(string num, int oldbase, int newbase){
	
	string ans = "";
	//开始进制转换
	int len = num.length();
	
	for(int i = 0; i < len;){//i是num的最高位的下标,(相当于普通整数(while(n > 0){})) 
		//
		int r = 0;//余数
		//开始求最后的余数(除基取余)
		//下面整个过程就是模拟笔算除法,从最高位开始 
		for(int j = i; j < len; j++){
			int rtemp = (r*oldbase+num[j]-'0') % newbase;
			num[j] = (r*oldbase+num[j]-'0') / newbase+'0'; 
			r = rtemp;
		} 
		ans += r+'0';
		while(num[i] == '0')i++;//去掉高位的0,更新数的长度以便于判断num是否为0 
	} 
	reverse(ans.begin(), ans.end());
	//最高位在string的低位存储 
	return ans;
}
int main(){
	string num;
	while(cin>>num){
		string ans = change(num, 10, 2);//将十进制转换为2进制
		cout<<ans<<endl; 
	}
	return 0;
} 

二进制数1380

北邮

  • 轻车熟路之后发现string接收结果处理结果也挺方便的
  • 还有:清北的题就是不一样
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
using namespace std;
/*

类型为unsigned int 类型的数字,
存储在计算机中的二进制串是什么样子的。 
你能帮帮小明吗?并且,小明不想要二进制串中前面的没有意义的0串,即要去掉前导0。
*/ 

string change(unsigned int n){
	//将n转化为二进制
	string ans = "";
	//除基取余法
	do{
		ans += n % 2 + '0';
		n /= 2;
	}while(n > 0);
	int len = ans.length();
	while(ans[len-1] == '0' && ans.length()>1){
		ans[len-1] = 0;
		len--;
	} 
	reverse(ans.begin(), ans.end());//小端存储最高位 
	return ans;
} 
int main(){
	unsigned int n;
	while(scanf("%u", &n) != EOF){
		string b = change(n);
		cout<<b<<endl; 
	}
	return 0;
}

八进制输入1417

  • 只是觉得自己太狡猾了
  • 华中科技大学机试题
#include<cstdio>
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		printf("%o\n", n);
	} 
	return 0;
} 

2022年3月21日

进制转换1422(m进制转n进制,适合大整数,进制2~36)

清华大学机试题

  • 所有结果都用string保存,最低位保存数字的最高位
  • 注意更新x[j]和判断x[i]==‘0’的时候要判断字符
  • 大整数的除法里层for循环可以多用几个变量,保存每一步的被除数(余数*oldbase+当前位),每一步的(被除数/newbase),每一步的余数(被除数%newbase)这个在for循环外面,最后一步的余数是转换后数的一位
  • 最后结果输出前要reverse一下,变为低位对应高位
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
/*
将M进制的数X转换为N进制的数输出。
输入的第一行包括两个整数:M和N(2<=M,N<=36)。
下面的一行输入一个数X,X是M进制的数,现在要求你将M进制的数X转换成N进制的数输出。
注意输入时如有字母,则字母为大写,输出时如有字母,则字母为小写。
*/
string change(string x, int m, int n){
	//m进制转n进制,除基取余法,
	string ncimal = "";
	int len = x.length();
	for(int i = 0; i < len;){
		int r = 0;//每一次除基之后的余数
		
		for(int j = i; j < len; j++){
			//得每一步的被除数 
			int t1;
			if(x[j] <= '9') t1 = r*m + x[j]-'0';
			else t1 = r*m + x[j]+10-'A';
			
			//得每一步的商,更新 
			int t2 = t1 / n;
			if(t2 <= 9){
				x[j] = t2 + '0';
			}else{
				x[j] = t2-10+'A';
			}
			
			//得每一步的余数 
			r = t1 % n;
		} 
		
		if(r <= 9){
			ncimal += r+'0';
		}else{
			ncimal += r-10+'a';
		}	
		while(x[i] == '0')i++;	
	} 
	return ncimal;
	 
}
int main(){
	//
	int m, n;
	string x;
	scanf("%d%d", &m, &n); 
	cin>>x;
	string ans = change(x, m, n);
	reverse(ans.begin(), ans.end());
	cout<<ans<<endl;
	return 0;
}

模拟题1097(负二进制)

  • 主要是负数除法的余数,保证是正数
  • 所以先算商,然后余数 = 被除数-商*除数
/*
1*(-2)^3+1*(-2)^2+0*(-2)^1+1*(-2)^0=-3,所以-3的该种转换为1101.
有多组测试数据。 输入为一个整数M.-100000<=M<=100000
*/
#include<cstdio>
using namespace std;
int num;
int main(){
	while(scanf("%d", &num) != EOF){
		vector<int> result;
		int r = 0;//保存余数
		int s = 0;//保存商
		//num是被除数 
		do{
			s = num / (-2);
			r = num - s * (-2);//余数=商*除数-被除数 
			if(r < 0){
				s = s+1;
				r =  num - s * (-2);
			}
			result.push_back(r);
			num = s;
		}while(num != 0);
		for(int i = result.size()-1; i >= 0; i--){
			printf("%d", result[i]);
		}
		printf("\n");
	}
	return 0;
}

排版类

输出菱形1473

  • 就是变量定义准确,思路清晰,找规律
/*
输入一个整数n表示棱形的对角半长度,请你用*把这个棱形画出来。

输入:1

输出:

*

输入:3

输出:

  *
 ***
*****
 ***
  *
*/
#include<cstdio>
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		for(int i = 1; i <= n; i++){
			//输出前n行
			//每一行n-i个空格,i+i-1个*
			int space = n-i;
			int star = i+i-1;
			while(space--){
				printf(" ");
			} 
			while(star--){
				printf("*");
			}
			printf("\n");
		}
		//输出后n-1行
		for(int i = 1; i <= n-1; i++){
			//i个space,n-i+(n-i-1)个*
			int space = i;
			int star = 2*(n-i)-1;
			while(space--){
				printf(" ");
			}
			while(star--){
				printf("*");
			}
			printf("\n");
		} 
	}
	return 0;
} 

输出杨辉三角(1062)

  • a[i][j] = a[i-1][j] + a[i-1][j+1];
  • 杨辉三角是一个等腰三角形
  • 需要初始化第一列为1,其他为0(可以用memset(arr, 0, sizeof(arr)))
  • %-d输出,是向左对齐
/*
输出杨辉三角
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1 


杨辉三角十个等腰三角形,数字等于头顶两个数字之和 
*/
#include<cstdio>
#include<cstring>


const int maxn = 25;
int arr[maxn][maxn];
int main(){
	
	int n;//n<=20 
	while(scanf("%d", &n) != EOF){
		memset(arr, 0, sizeof(arr));
		//a[i][j] = a[i-1][j] + a[i-1][j+1];
		//初始化第一列
		for(int i = 0; i < n; i++){
			arr[i][0] = 1;
		} 
		//计算其他
		for(int i = 1; i < n; i++){
			for(int j = 1; j <= i; j++){
				arr[i][j] = arr[i-1][j] + arr[i-1][j-1]; 
			}
		} 
		//输出
		for(int i = 0; i < n; i++){
			for(int j = 0; j <= i; j++){
				if(j < i){
					printf("%d ", arr[i][j]);
				}else{
					printf("%d", arr[i][j]);
				}
			}
			printf("\n");
		} 
		
	}
	return 0;
} 

输出杨辉三角(1392递归)

/*
递归求杨辉三角
 
*/
#include<cstdio>
#include<cstring>
const int maxn = 25;
int a[maxn][maxn] = {0};

int yhdg(int i, int j){
	if(j == 0 || i == j){
		a[i][j] = 1;
		return 1;
	}
	return yhdg(i-1, j-1) + yhdg(i-1, j);
}
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		memset(a, 0, sizeof(a));
		for(int i = 1; i < n; i++){
			for(int j = 0; j <= i; j++){
				if(j < i){
					printf("%d ", yhdg(i, j));
				}else{
					printf("%d", yhdg(i, j));
				}
			}
			printf("\n");
		}
	}
	return 0;
} 

旋转矩阵(1377)

北航

  • 耶~
/*
任意输入两个9阶以下矩阵,
要求判断第二个是否是第一个的旋转矩阵,
如果是,输出旋转角度(0、90、180、270),
如果不是,输出-1。
输入有多组数据。
每组数据第一行输入n(1<=n<=9),从第二行开始输入两个n阶矩阵
3
1 2 3
4 5 6
7 8 9
7 4 1
8 5 2
9 6 3

00 01 02
02 12 22

10 11 12
01 11 22



输出:
90 
*/
#include<cstdio>

const int maxn = 10;
 
void in(int arr[][maxn], int n){
	for(int i = 0 ; i < n; i++){
		for(int j = 0; j < n; j++){
			scanf("%d", &arr[i][j]);
		}
	}
}
bool isequal(int a1[][maxn], int a2[][maxn], int n){
	 for(int i = 0; i < n; i++){
	 	for(int j = 0; j < n; j++){
		 	if(a1[i][j] != a2[i][j]){
				return false; 	
			}
		}
	 }
	return true;
}
void trans(int a[][maxn], int n){
	//将a顺时针旋转90°
	int b[maxn][maxn] = {0};
	//一行一行的放置
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			b[j][n-i-1]  = a[i][j];
		}
	} 
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			a[i][j]  = b[i][j];
		}
	} 
	 
}
void out(int a[][maxn], int n){
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
}
int main(){
	int a[maxn][maxn];
	int b[maxn][maxn];
	
	int n;
	while(scanf("%d", &n) != EOF){
		//
		in(a, n);
		in(b, n);
		//
//		out(a, n);
//		out(b, n);
		int ans = -1;
		for(int i = 1; i <= 4; i++){
			if(isequal(a, b, n)){
				if(i == 1) ans = 0;
				else if(i == 2) ans = 90;
				else if(i == 3) ans = 180;
				else if(i == 4) ans = 270;
				break;
			}
			trans(a, n);
		} 
		printf("%d\n", ans);
		
	}
	
	return 0;
} 

旋转矩阵(1216)

  • 找规律,确定需要的参数
  • 递归分治
  • (之前做过,竟然忘了)
/*
输入一个整数n(1 <= n <= 20), n为方阵的行数。
输入5
输出:
1   16  15  14  13
2   17  24  23  12
3   18  25  22  11
4   19  20  21  10
5   6   7   8   9 
*/ 
#include<cstdio>
const int maxn = 21;
int arr[maxn][maxn];
//参数  要填的数字,左上角的坐标,要填的矩阵规模 
void fill(int count, int k, int scale){
	if(scale == 0){
		return;
	}
	if(scale == 1){
		arr[k][k] = count;
		return;
	}
	int i, j;//数组横纵坐标 
	i = j = k;
	//左上--左下
	int len = scale-1;
	while(len--){
		arr[i++][j] = count++;
	} 
	len = scale-1;
	//左下--右下
	while(len--){
		arr[i][j++] = count++;
	}
	len = scale-1;
	//右下--右上 
	while(len--){
		arr[i--][j] = count++; 
	}
	len = scale-1;
	while(len--){
		arr[i][j--] = count++;
	}
	fill(count, k+1, scale-2);
}
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		//使用分治法
		int count = 1;
		fill(count, 0, n);
		//输出
		for(int i = 0; i < n; i++){
			for(int j = 0; j < n; j++){
				printf("%-4d", arr[i][j]);	
			}
			printf("\n");
		}  
	}
	return 0;
} 

2022年3月22日

日期类

  • 输入的时候一般用scanf()解析输入

日期计算(1051)

  • 定义dayOfMonth
  • 闰年:y %400 == 0 || (y % 4 == 0 && y % 100 != 0)
  • 判断输入是否合法
  • continue
/*
定义一个结构体变量(包括年、月、日),编程序,要求输入年月日,计算并输出该日
在本年中第几天。
输入三个整数(并且三个整数是合理的,既比如当输入月份的时候应该在1 至12 之间,
 不应该超过这个范围)否则输出Input error!
 输出一个整数.既输入的日期是本年的第几天。
 1985 1 20
 2006 3 12

 20
 71

*/
#include<cstdio>
struct date {
	int year;
	int month;
	int day;
};
int daysOfMonth[2][13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
                          0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
                         };//闰年
bool isleap(int y) {
	return y %400 == 0 || (y % 4 == 0 && y % 100 != 0);
}
int main() {
	date d;
	
	while(scanf("%d %d %d", &d.year, &d.month, &d.day) != EOF) {
		int flag = 0;//平年是0
		if(isleap(d.year)) {
			flag = 1;
		}
		
		if(d.year < 1 || d.month < 1 || d.month > 12 || d.day < 1 || d.day > daysOfMonth[flag][d.month]) {
			printf("Input error!\n");
			continue;
		}

		//输入合法
		int ans = 0;
		for(int i = 1; i < d.month; i++) {
			ans += daysOfMonth[flag][i];
		}
		ans += d.day;
		printf("%d\n", ans);

	}
	return 0;
}

日期差值(1290)

  • dOfM[2][13]数组的定义
  • 可以一次性把所有的计算出来d[5000][13][32]
  • 输入格式%4d%2d%2d取出年月日
/*
有两个日期,求两个日期之间的天数,如果两个日期是连续的我们规定他们之间的天数为两天
有多组数据,每组数据有两行,分别表示两个日期,形式为YYYYMMDD
20110412
20110422

11

*/

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 5000;
struct date{
	int year;
	int month;
	int day;
	int days;
}dates[maxn];
int d[5001][13][32];
int dOfM[2][13] = {
	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
	0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
bool isleap(int y){
	return y % 400 == 0 || (y % 4 == 0 && y % 100 != 0);
}
void cal(){
	int day = 0;
	for(int i = 0; i < maxn; i++){
		int flag = 0;
		if(isleap(i)){
			flag = 1;
		}
		for(int j = 1; j <= 12; j++){
			for(int k = 1; k <= dOfM[flag][j]; k++){
				d[i][j][k] = day++;
			}
		}
	}
}
int main(){
	cal();
	int  y1, m1, d1, y2, m2, d2;
	while(scanf("%4d%2d%2d", &y1, &m1, &d1) != EOF){
		scanf("%4d%2d%2d", &y2, &m2, &d2);
		printf("%d\n", abs(d[y1][m1][d1]- d[y2][m2][d2])+1);
	}
	return 0;	
} 

别太把自己当回事儿,
也别太不把自己当回事儿
不要麻烦别人,你自己可以!

打印日期(1410)

  • 及时break出去
  • 输出格式
/*
给出年分m和一年中的第n天,算出第n天是几月几号
2000 3
2000 31
2000 40
2000 60
2000 61
2001 60


2000-01-03
2000-01-31
2000-02-09
2000-02-29
2000-03-01
2001-03-01
*/
#include<cstdio>
using namespace std;
int dOfM[2][13] = {
	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
	0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
bool isleap(int y){
	return y % 400 == 0 || (y % 4 == 0 && y % 100 != 0);
}

int main() {
	int y, d;
	while(scanf("%d %d", &y, &d) != EOF){
		int month = 1, day; 
		int flag = 0;//平年
		if(isleap(y)){
			flag = 1;
		} 
		//开始计算月份和天
		for(int i = 1; i <= 12; i++){
			if(d > dOfM[flag][i]){
				d -= dOfM[flag][i];
				month ++;
			}else{
				break;
			}
		} 
		day = d;
		printf("%4d-%02d-%02d\n", y, month, day);
	}
	return 0;
}

日期类(1437加一天)

  • 类的编写和对象的创建
/*
编写一个日期类,要求按xxxx-xx-xx 的格式输出日期,实现加一天的操作。
2
1999 10 20
2001 1 31

1999-10-21
2001-02-01
输入第一行表示测试用例的个数m,
接下来m行每行有3个用空格隔开的整数,分别表示年月日。
测试数据不会有闰年。
*/
#include<cstdio>
int dOfM[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
class date{
	public:
		int y;
		int m;
		int d;
	void nextDay(){
		if(++d > dOfM[m]){
			d = 1;
			m++;
			if(m > 13){
				m = 1;
				y++;
			} 
		}
	} 
};

int main(){
	int n; 
	scanf("%d", &n);
	for(int i = 0; i < n; i++){
		date myDate;
		scanf("%d%d%d", &myDate.y, &myDate.m, &myDate.d);
		myDate.nextDay();
		printf("%04d-%02d-%02d\n", myDate.y, myDate.m, myDate.d);	
	}
	return 0;
}

日期累加(1446)

  • 就是if语句让day++,判断,month++,判断,year++
/*
设计一个程序能计算一个日期加上若干天后是什么日期。
输入第一行表示样例个数m,接下来m行每行四个整数分别表示年月日和累加的天数。
1
2008 2 3 100

2008-05-13

*/
#include<cstdio>
using namespace std;
int dOfM[2][13] = {
	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
	0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
bool isleap(int y){
	return y % 400 == 0 || (y % 4 == 0 && y % 100 != 0);
}
class date{
	public:
		int y;
		int m;
		int d;
	void nextn(int n){
		//n天之后的日期
		while(n--){
			int flag = 0;
			if(isleap(y)){
				flag = 1; 
			}
			++d;
			if(d > dOfM[flag][m]){
				d = 1;
				m++;
				if(m > 12){
					m = 1;
					y++;
				}
			}
		}
		printf("%04d-%02d-%02d\n", y, m, d);
	}
};
int main(){
	int n;
	scanf("%d", &n);
	while(n--){
		date mydate;
		int next;
		scanf("%d%d%d%d", &mydate.y, &mydate.m, &mydate.d, &next);
		mydate.nextn(next);
	}
	return 0;
} 

偷菜时间表(1053)

  • 写麻辣,再写一道我就出去
/*
假设当前时间为13:15,第一行输入作物种类数n,
从第二行开始输入n 种作物成熟需要的时间,格式为
Hour:Minute。
依次输出n 种作物成熟时间,每行输出一个
3
0:30
1:10
12:50

13:45
14:25
2:5
*/
#include<cstdio>
int main(){
	
	int n;
	scanf("%d", &n);
	while(n--){
		int nowh = 13;
		int nowm = 15;
		int h, m;
		scanf("%d:%d", &h, &m);
		nowm += m;
		if(nowm  >= 60){
			nowm -= 60;
			nowh++;
		}
		nowh += h;
		while(nowh >= 24){
			nowh -= 24;
		}
		printf("%d:%d\n", nowh, nowm);
	}
	return 0;
} 

字符串类

加密算法(秒1014)

  • 输入有空格的话用getlin(cin, string)
/*
编写加密程序,加密规则为:将所有字母转化为该字母后的第三个字母,
即A->D、B->E、C->F、......X-A、Y->B、Z->C。
小写字母同上,其他字符不做转化。
输入任意字符串,输出加密后的结果。

例如:输入"I love 007",输出"L oryh 007"
输入一行字符串,长度小于100。

*/
#include<cstdio>
#include<string>
#include<iostream>
#include<cstring>
using namespace std;
 
int main(){
	string s;
	getline(cin, s);
	for(int i = 0; i < s.length(); i++){
		if((s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z')){
			if((s[i] >= 'a' && s[i] <= 'w') || (s[i] >= 'A' && s[i] <= 'W')){
				s[i] = s[i] + 3;
			}else{
				s[i] = s[i] + 3 - 26;
			}
		}
	}
	cout<<s<<endl;
	
	return 0;
}

字符移动(秒1012)

  • 有的问题,先思考两秒钟,好简单
/*
输入一个字符串,将其中的数字字符移动到非数字字符之后,
并保持数字字符和非数字字符输入时的顺序。
例如:输入字符串“ab4f35gr#a6”,输出为“abfgr#a4356”。

*/
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
int main(){
	string s;
	cin>>s;
	string sd = "";
	string ans = "";
	for(int i = 0; i < s.length(); i++){
		if(s[i] >= '0' && s[i] <= '9'){
			sd += s[i];
		}else{
			ans += s[i];
		}
	}
	ans += sd;
	cout<<ans;
	return 0;
} 

碎碎念:再写一道去跑步

字母统计(1292秒)

  • 就是使用遍历和哈希
/*
输入一行字符串,计算其中A-Z大写字母出现的次数
案例可能有多组,每个案例输入为一行字符串。


DFJEIWFNQLEF0395823048+_+JDLSFJDLSJFKK

输出样例#:

A:0
B:0
C:0
D:3
E:2
F:5
G:0
H:0
I:1
J:4
K:2
L:3
M:0
N:1
O:0
P:0
Q:1
R:0
S:2
T:0
U:0
V:0
W:1
X:0
Y:0
Z:0
*/
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
const int maxn = 27;
 
int main(){
	string s;
	while(getline(cin, s)){
		int character[maxn] = {0};
		//输入非string 或 EOF终止
		for(int i = 0; i < s.length(); i++){
			if(s[i] >= 'A' && s[i] <= 'Z'){
				character[s[i]-'A']++;
			}
		}
		for(int i = 0; i < 26; i++){
			printf("%c:%d\n", 'A'+i, character[i]);
		}
	}
	return 0;
} 

碎碎念:再做一道

首字母大写(1240秒)

北大机试题

  • some of LIKE yoU. www kkk, ffff. 这种本来就是大写的
  • 还有首字母的处理
/*
对一个字符串中的所有单词,
如果单词的首字母不是大写字母,
则把单词的首字母变成大写字母。 
在字符串中,单词之间通过空白符分隔,
空白符包括:空格(' ')、制表符('\t')、回车符('\r')、换行符('\n')。



if so, you already have a google account. you can sign in on the right.



If So, You Already Have A Google Account. You Can Sign In On The Right.


*/
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
int main(){
	string s;
	while(getline(cin, s)){
		int i = 0;
		while(s[i++] == ' ');
		if(s[i-1] > 'Z'){
			s[i-1] -= ('a'-'A');
		}
		 
		for(; i < s.length(); i++){
			if(s[i] != ' ' && s[i-1] == ' '){
				if(s[i] > 'Z')
				s[i] -= ('a'-'A');
			}
		}
		cout<<s<<endl;
	}
	
	return 0;
} 

碎碎念:再做一道去跑步

统计单词(11′1394)

  • 特殊输入hello how are you you oooo .
  • 注意count为0的时候不输出
/*
编一个程序,读入用户输入的,以“.”结尾的一行文字,
统计一共有多少个单词,并分别输出每个单词含有多少个字符。 
(凡是以一个或多个空格隔开的部分就为一个单词)
hello how are you.
5 3 3 3 
*/
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;

int main(){
	string s;
	while(getline(cin, s)){
		int count = 0;
		for(int i = 0; s[i] != '.'; i++){
			if(s[i] == ' '){
				if(count != 0){
					printf("%d ", count);
				}
				count = 0;
				continue;
			}else{
				count++;
			}
		}
		if(count != 0){
			printf("%d\n", count);
		}else{
			printf("\n");
		}
	}
	return 0;
} 

碎碎念:再写一道去跑步

删除字符串2(1027)

  • stirng库文件 erase(its, ite) [) erase(pos, len)
  • string库文件 find(substr) find(substr, pos) 找不到返回string::npos
  • 其他:substr(pos, len)
  • clear()
  • insert(init, its, ite) insert(pos, string)
  • replace(its, ite, string) replace(pos, len, string)
  • string::iterator it;
/*
给你一个字符串S,要求你将字符串中出现的所有"gzu"(不区分大小写)子串删除,
输出删除之后的S。

就是说出现“Gzu”、“GZU”、“GZu”、"gzU"都可以删除。
GzzGzukkgzUuu
Gzzkkuu
*/
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
int main(){
	//使用string 的find(substr, pos);   erase(pos, len);
	string s;
	while(getline(cin, s)){
		
		string s1[8] = {"Gzu", "GZu", "GZU", "GzU", "gzu", "gZu", "gZU", "gzU"}; 
		for(int i = 0; i < 8; i++){
			int pos;
			while((pos = s.find(s1[i])) != string::npos){
				s.erase(pos, 3);
			}
		}
		cout<<s<<endl;
	} 
	return 0;
} 

碎碎念:我还没有去跑步
啊跑完了

2022年3月23日

排序类问题

  • 一个sort()走天下
  • 自定义函数排序
  • 多级排序
  • 归并排序(逆序数对个数
  • 选择排序(选择前几大、小的数)
  • 快速排序(找第k大、小)

成绩排序(1151)10′

清华大学上机

  • 多一个index,在成绩相同时二级排序,sort()排序不稳定
  • 或者用stable_sort()稳定排序
/*
输入任意(用户,成绩)序列,
可以获得成绩从高到低或从低到高的排列,相同成绩
都按先录入排列在前的规则处理。

示例:
jack      70
peter     96
Tom       70
smith     67

从高到低  成绩
peter     96
jack      70
Tom       70
smith     67

从低到高

smith     67
jack      70
Tom      70
peter     96
输入输出格式
输入描述:

输入多行,先输入要排序的人的个数,
然后输入排序方法0(降序)或者1(升序)再分别输入他们的名字和成绩,以一个空格隔开

输出描述:

按照指定方式输出名字和成绩,名字和成绩之间以一个空格隔开

输入输出样例
输入样例#:

3
0
fang 90
yang 50
ning 70

输出样例#:
复制

fang 90
ning 70
yang 50


*/
#include<cstdio>
#include<algorithm>
#include<string>
#include<vector>
#include<iostream>
using namespace std;
struct info{
	string name;
	int grade;
	//int index;
};
bool cmp0(info a, info b){
//	if(a.grade != b.grade){
//		return a.grade > b.grade;	
//	}else{
//		return a.index < b.index;
//	}
	return a.grade > b.grade;	
	
}
bool cmp1(info a, info b){
//	if(a.grade != b.grade)
//		return a.grade < b.grade;
//	else{
//		return a.index < b.index;
//	}
	return a.grade < b.grade;
}
int main(){
	int n, way;
	scanf("%d%d", &n, &way);
	
	vector<info> infos;
	for(int i = 0; i < n; i++){
		info temp;
		cin>>temp.name>>temp.grade;
		//temp.index = i;
		infos.push_back(temp); 
	} 
	if(way == 0){
		stable_sort(infos.begin(), infos.end(), cmp0);
	}else{
		stable_sort(infos.begin(), infos.end(), cmp1);
	}
	for(int i = 0; i < infos.size(); i++){
		cout<<infos[i].name<<" "<<infos[i].grade<<endl;
	}
	return 0;
} 

各种排序(1106要吐了)

  • 插入排序,[0, i-1]是已经排好序的,[i, n-1]是为排好序的,把v[i]插入到已排好序的序列中
  • 希尔排序,增量d从len/2一直到1,在每组内选择排序,指针j指向空位置
  • 选择排序,[0, i-1]已经排好序,在[i, n-1]中选择一个最小的,放到v[i]的位置,
  • 快速排序,需要一个枢轴,而且传入参数vector&v,需要用引用,不然改不了,partition(v, left, right)内部是双指针法,
  • 归并排序,参数也用引用,merge(v, l1, r1, l2, r2)内部是双指针法,而且还要再开辟一个数组存放结果,最后赋给v的时候注意v[l1+i] = tv[i],且长度是tv.size()
  • 而且归并排序是用二分的思想,但是分的形式并不是先22分,而是先分成2组,再继续分
  • 所以一次二分排序还是使用for循环来实现
/*
编写程序实现
直接插入排序、
希尔排序(d=5)、
直接选择排序、
快速排序和
二路归并排序算法。

输出:
直接插入排序后的结果
一趟希尔排序后的结果
直接选择排序后的结果
快速排序后的结果
一趟二路归并排序后的结果


10
50 36 41 19 23 4 20 18 12 22


4 12 18 19 20 22 23 36 41 50
4 20 18 12 22 50 36 41 19 23
4 12 18 19 20 22 23 36 41 50
4 12 18 19 20 22 23 36 41 50
36 50 19 41 4 23 18 20 12 22

*/
#include<cstdio>
#include<vector>
using namespace std;
//从一堆鸡蛋里面拿第一个往框里面放 
void insertSort(vector<int> v){
	//插入排序,前[0,i-1]有序,后[i, n-1]无序
	//从后往前在[0, i-1]中,不断后移大于等于[i]的元素,
	//把位置j腾出来,放入[i]
	
	
	for(int i = 1; i <v.size(); i++){//初始第1个元素有序,一共进行n-1趟查找插入的位置j
		int temp = v[i];//暂时存储要插入的元素
		int j = i;//指向空的位置 
		while(j >= 1 && v[j-1] >= temp){
			v[j] = v[j-1];
			j--;
		} 
		
		v[j] = temp;
	} 
	for(int i = 0; i < v.size(); i++){
		printf("%d ", v[i]);
	}
	printf("\n");
}
void onceShellSort(vector<int> v){
	//算法先将要排序的一组数按某个增量d分成若干组,
	//每组中记录的下标相差d.对每组中全部元素进行排序,
	//然后再用一个较小的增量对它进行分组,在每组中再进行排序。
	//当增量减到1时,整个要排序的数被分成一组,排序完成。
	//一般的初次取序列的一半为增量,以后每次减半,直到增量为1。
	//for(int d = v.size()/2; d; d /= 2){//增量每次减半
		//对每一组进行插入排序
		int d = v.size() / 2;
		
		for(int i = d; i < v.size(); i ++){
			//初始第0个已经有序
			int j = i;//指向空出来的位置 
			int temp = v[i];//暂时保存需要放置的元素v[i],即无序部分的第一个
			while(j >= d && v[j-d] >= temp){
				v[j] = v[j-d];
				j -= d;
			} 
			v[j] = temp;
		} 
		
//	}
	for(int i = 0; i < v.size(); i++){
		printf("%d ", v[i]);
	} 
	printf("\n");
}
//从一筐鸡蛋里面找一个 
void selectSort(vector<int> v){
	//从待排序部分中选择最小的元素放在待排序的第一个位置
	for(int i = 0; i < v.size(); i++){//待排序部分[i, n-1]
		int min = i;// 保存最小元素的下标
		for(int j = i+1; j < v.size(); j++){//找最小的元素 
			if(v[j] < v[min]){
				min = j;
			}
		} 
		//min位置和i位置交换
		int temp = v[min];
		v[min] = v[i];
		v[i] = temp; 	
	} 
	for(int i = 0; i < v.size(); i++){
		printf("%d ", v[i]);
	} 
	printf("\n");	
}
int partition(vector<int> &v, int left, int right){
	int k = v[left];//枢轴
	//交换左右
	while(left < right){//左右相遇时左右就是枢轴的位置 
		while(left < right && v[right] >= k) 
			right--;
		v[left] = v[right];
		while(left < right && v[left] <= k) 
			left++;
		v[right] = v[left];
		
	} 
	v[left] = k;
	return left;
}
/*
10
50 36 41 19 23 4 20 18 12 22
*/
void quickSort1(vector<int> &v, int left, int right){
	if(left < right){//元素个数>1 
		int p = partition(v, left, right);
		quickSort1(v, left, p-1);
		quickSort1(v, p+1, right);
	}
} 
void quickSort(vector<int> v){
	quickSort1(v, 0, v.size()-1);
		for(int i = 0; i < v.size(); i++){
			printf("%d ", v[i]);
		} 
		printf("\n");
}
void merge(vector<int> &v, int l1, int r1, int l2, int r2){
	vector<int> tv;
	int i = l1, j = l2;
	while(i <= r1 && j <= r2){
		if(v[i] <= v[j]){
			tv.push_back(v[i++]);
		}else{
			tv.push_back(v[j++]);
		}
	}
	while(i <= r1) tv.push_back(v[i++]);
	while(j <= r2) tv.push_back(v[j++]);
	for(int i = 0; i < tv.size(); i++){
		v[l1+i] = tv[i];
	}
}
void mergeSort1(vector<int>&v, int left, int right){
	
	if(left < right){//待排序个数>1
		int mid = (left + right) / 2;
		mergeSort1(v, left, mid);
		mergeSort1(v, mid+1, right);
		merge(v, left, mid, mid+1, right); 	
	}	
}
void mergeSort(vector<int> v){
	mergeSort1(v, 0, v.size()-1);
	for(int i = 0; i < v.size(); i++){
		printf("%d ", v[i]);
	} 
	printf("\n");
}

void divideonce(vector<int> v){
	for(int i = 0; i < v.size()-1; i+=2){
		if(v[i] > v[i+1]){
			swap(v[i], v[i+1]);
		}
	}
	for(int i = 0; i < v.size(); i++){
		printf("%d ", v[i]);
	} 
	printf("\n");
}
int main() {
	int n;
	while(scanf("%d", &n) != EOF) {
		vector<int> num;
	 
		for(int i = 0; i < n; i++) {
			int temp;
			scanf("%d", &temp);
			num.push_back(temp);
		}
		insertSort(num);
		onceShellSort(num);
		selectSort(num);
		quickSort(num);
		//mergeSort(num);
		divideonce(num);
	}
	return 0;
}

成绩排序2.0(1159秒)

清华大学上机题
让用一位数组实现,好像就是一个二级排序,跟第一个一样

/*
用一维数组存储学号和成绩,然后,按成绩排序输出。
输入第一行包括一个整数N(1<=N<=100),代表学生的个数。
接下来的N行每行包括两个整数p和q,分别代表每个学生的学号和成绩。
按照学生的成绩从小到大进行排序,并将排序后的学生信息打印出来。
如果学生的成绩相同,则按照学号的大小进行从小到大排序。


3
1 90
2 87
3 92

2 87
1 90
3 92

*/
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 110;
struct info{
	int no;
	int grade;
}infos[maxn];
bool cmp(info a, info b){
	if(a.grade == b.grade){
		return a.no < b.no;
	}
	return a.grade < b.grade;
}
int main(){
	int n;
	scanf("%d", &n);
	for(int i = 0; i < n; i++){
		scanf("%d%d", &infos[i].no, &infos[i].grade);
	}
	sort(infos, infos+n, cmp);
	for(int i = 0; i < n; i++){
		printf("%d %d\n", infos[i].no, infos[i].grade);
	}
	return 0; 
} 

国名排序(1217)

  • sort可以默认对string进行排序,但对char[]需要实现cmp方法,用strcmp(ca, cb),ca小于cb的时候返回负数。
/*


3
China
Canada
America



America
Canada
China


*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 110;
//bool cmp(string a, string b){
//	return strcmp(a.c_str(), b.c_str()) < 0;
//}
int main(){
	int n;
	cin>>n;
	vector<string> counties;
	for(int i = 0; i < n; i++){
		string ts;
		cin>>ts;
		counties.push_back(ts);
	}
	sort(counties.begin(), counties.end());
	for(int i = 0; i < n; i++){
		cout<<counties[i]<<endl;
	}
	return 0;
} 

日志排序(1227哭死)

  • 以空白行结束的判断
  • 细节!!!!!!
/*
 “hs_10000_p”是计算任务的名称,
 “2007-01-17 19:22:53,315”是计算任务开始执行的时间“年-月-日 时:分:秒,毫秒”,
  “253.035(s)”是计算任务消耗的时间(以秒计)

  hs_10000_p 2007-01-17 19:22:53,315 253.035(s)
  请你写一个程序,对日志中记录计算任务进行排序。
   时间消耗少的计算任务排在前面
  时间相同,则将开始执行时间早的计算任务排在前面。

  日志中每个记录是一个字符串,每个字符串占一行。最后一行为空行,表示日志结束。日志中最多可能有10000条记录。
  计算任务名称的长度不超过10,开始执行时间的格式是YYYY-MM-DD HH:MM:SS,MMM,消耗时间小数点后有三位数字。
  计算任务名称与任务开始时间、消耗时间之间以一个或多个空格隔开,行首和行尾可能有多余的空格。


hs_10000_p   2007-01-17 19:22:53,315     253.035(s)
hs_10001_p   2007-01-17 19:22:53,315     253.846(s)
hs_10002_m   2007-01-17 19:22:53,315     129.574(s)
hs_10002_p   2007-01-17 19:22:53,315     262.531(s)
hs_10003_m   2007-01-17 19:22:53,318     126.622(s)
hs_10003_p   2007-01-17 19:22:53,318     136.962(s)
hs_10005_m   2007-01-17 19:22:53,318     130.487(s)
hs_10005_p   2007-01-17 19:22:53,318     253.035(s)
hs_10006_m   2007-01-17 19:22:53,318     248.548(s)
hs_10006_p   2007-01-17 19:25:23,367    3146.827(s)



  hs_10003_m   2007-01-17 19:22:53,318     126.622(s)
  hs_10002_m   2007-01-17 19:22:53,315     129.574(s)
  hs_10005_m   2007-01-17 19:22:53,318     130.487(s)
  hs_10003_p   2007-01-17 19:22:53,318     136.962(s)
  hs_10006_m   2007-01-17 19:22:53,318     248.548(s)
  hs_10000_p   2007-01-17 19:22:53,315     253.035(s)
  hs_10005_p   2007-01-17 19:22:53,318     253.035(s)
  hs_10001_p   2007-01-17 19:22:53,315     253.846(s)
  hs_10002_p   2007-01-17 19:22:53,315     262.531(s)
  hs_10006_p   2007-01-17 19:25:23,367    3146.827(s)


*/
#include<cstdio>
#include<string>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
struct mylog {
	string s;
	string name = "";
	string date = "";
	string time = "";
	string cost = "";
};
vector<mylog> logs;
bool cmp(mylog a, mylog b) {
	if(a.cost == b.cost) {
		return a.date+a.time < b.date + b.time;
	}
	return a.cost < b.cost;
}
int main() {
	//最后一行为空行,表示日志结束
	string s = "";
	while(getline(cin, s) && s.size()) {
		
		mylog l;
		l.s = s;
		int len = l.s.length();
		int j = 0;
		int i = 0;
		for(; i < l.s.length(); i++) {
			if(l.s[i] != ' ') {
				l.name += l.s[i];
			}else{
				break;
			}
		}
		//cout<<l.name<<endl;
		while(l.s[i] == ' ') i++;
		for(; i < l.s.length(); i++) {
			if(l.s[i] != ' ')
				l.date += l.s[i];
			else{
				break;
			}
		}
	//	cout<<l.date<<endl;
		while(l.s[i] == ' ') i++;
		j = 0;
		for(; i < l.s.length(); i++) {
			if(l.s[i] != ' ')
				l.time += l.s[i];
			else{
				break;
			}
		}
	//	cout<<l.time<<endl;
		while(l.s[i] == ' ') i++;
		for(; i < l.s.length(); i++) {
			if(l.s[i] != ' ')
				l.cost += l.s[i];
			else{
				break;
			}
		}
	//	cout<<l.cost<<endl;
		logs.push_back(l);
		s.clear();
	}
	sort(logs.begin(), logs.end(), cmp);
	for(int i = 0; i < logs.size(); i ++){
		cout<<logs[i].s<<endl;
	}
		return 0;
}

奇偶排序(1246秒)

/*
输入10个整数,彼此以空格分隔。重新排序以后输出(也按空格分隔),要
求: 1.先输出其中的奇数,并按从大到小排列; 
2.然后输出其中的偶数,并按从小到大排列。

*/
#include<cstdio>
#include<string>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
bool cmp(int a, int b){
	return a > b;
}
int main(){
	int num[10];
	while(scanf("%d", &num[0]) != EOF){
		vector<int> odd;
		vector<int> even;
		if(num[0] & 1){
			odd.push_back(num[0]);
		}else{
			even.push_back(num[0]);
		}
		for(int i = 1; i < 10; i++){
			scanf("%d", &num[i]);
			if(num[i] & 1){
				odd.push_back(num[i]);
			}else{
				even.push_back(num[i]);
			}
		} 
		sort(odd.begin(), odd.end(), cmp);
		sort(even.begin(), even.end());
		for(int i = 0; i < odd.size(); i++){
			printf("%d ", odd[i]);
		}
		for(int i = 0; i < even.size(); i++){
			printf("%d ", even[i]);
		}
		printf("\n");
	}
	return 0;
} 

2022年3月24日

17:26:55,今日睡眠【5:21—8:30】【13:05—17:10】

字符串排序(1254秒)

为什么会有这样的机试题

/*

输入一个长度不超过20的字符串,对所输入的字符串,
按照ASCII码的大小从小到大进行排序,请输出排序后的结果
dcba
abcd 
*/ 
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
int main(){
	string s;
	while(cin>>s){
		sort(s.begin(), s.end());
		cout<<s<<endl;
	}
	return 0;
}

字符排序2(1255)

差距

  • 利用空间简化了题目?
  • 不是很难
/*
规则 1 :英文字母从 A 到 Z 排列,不区分大小写。

如,输入: Type 输出: epTy

规则 2 :同一个英文字母的大小写同时存在时,按照输入顺序排列。

如,输入: BabA 输出: aABb

规则 3 :非英文字母的其它字符保持原来的位置。

如,输入: By?e 输出: Be?y

样例:

输入:

A Famous Saying: Much Ado About Nothing(2012/8).

输出:

A aaAAbc dFgghh : iimM nNn oooos Sttuuuy (2012/8).
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector> 
#include<iostream>
using namespace std;
struct character{
	char c;//字符 
	int index;//下标 
	bool isc;//是否是字符 
	char t;
};
bool cmp(character a, character b){
	if(a.c > 'Z'){
		a.t -= ('a'-'A');
	}
	if(b.c > 'Z'){
		b.t -= ('a'-'A');
	}
	
	if(a.t == b.t){
		//两个字符相等,或者是大小写关系
		return a.index < b.index; 
	}
	return a.t < b.t;
}
int main(){
	vector<character> str;
	vector<character> purec;
	string s;
	while(getline(cin, s)){
		for(int i = 0; i < s.length(); i++){
			character one;
			one.c = s[i];
			one.t = one.c;
			one.index = i;
			if((s[i] <= 'z' && s[i] >= 'a') ||(s[i] <= 'Z' && s[i] >= 'A')){
				one.isc = true;
				purec.push_back(one);
			}else{
				one.isc = false;
			}
			str.push_back(one);
		}
		sort(purec.begin(), purec.end(), cmp);
//		for(int i = 0; i < purec.size(); i++){
//			printf("%c", purec[i].c);
//		}
//		printf("\n");
		int j = 0;
		for(int i = 0; i < str.size(); i++){
			if(str[i].isc){
				str[i].c = purec[j++].c;
			}
			printf("%c", str[i].c);
		}
		printf("\n");
	}
	return 0;
}

字符排序3(1261)

北大上机

  • 前面接收的是n,后面那个换行getline()会接收,所以中间加一个getchar()
/*
字符串的个数,以及该组字符串。
每个字符串以‘\n’结束。
如果输入字符串为“stop”,也结束输入.

*/
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector> 
#include<iostream>
using namespace std;
bool cmp(string a, string b){
	return a.length() < b.length();
}
int main(){
	int n;
	vector<string> ss; 
	while(cin>>n){
		getchar();
		for(int i = 0; i < n; i++){
			string s;
			getline(cin, s);
			if(s == "stop"){
				break;
			}
			ss.push_back(s);
		}

		//排序
		sort(ss.begin(), ss.end(), cmp);
		//输出
		for(int i = 0; i < ss.size(); i++){
			cout<<ss[i]<<endl;
		} 
		ss.clear();
	}
	return 0;
} 

2022年3月25日00:06:15,今天跑步13.16km,现在睡觉。晚安。

2022年3月25日

2022年3月25日08:23:50。终于收拾好了[00:23, 07:14]

后缀子串排序(1294秒)

  • 用到string的substr(start, end+1)函数
/*
对于一个字符串,将其后缀子串进行排序,
例如grain 
其子串有: grain rain ain in n 然后对各子串按字典顺序排序,
即: ain,grain,in,n,rain
*/ 
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
int main(){
	string s;
	while(cin>>s){
		vector<string> subs;
		int len = s.length();
		for(int i = 0; i < len; i++){
			string sub = s.substr(i, len);
			subs.push_back(sub);
		}
		sort(subs.begin(), subs.end());
		for(int i = 0; i < subs.size(); i++){
			cout<<subs[i]<<endl;
		}
	}
	return 0;
} 

EXCEL排序(1338秒)

  • 还是cmp函数的写法,二级排序
/*
测试输入包含若干测试用例。
每个测试用例的第1行包含两个整数
 N (N<=100000) 和 C,其中 N 是纪录的条数,C 是指定排序的列号。
 以下有N行,每行包含一条学生纪录。
 每条学生纪录由学号(6位数字,同组测试中没有重复的学号)、
 姓名(不超过8位且不包含空格的字符串)、
 成绩(闭区间[0, 100]内的整数)组成,
每个项目间用1个空格隔开。
当读到 N=0 时,全部输入结束,相应的结果不要输出。

当 C=1 时,按学号递增排序;
当 C=2时,按姓名的非递减字典序排序;
当 C=3 时,按成绩的非递减排序。当若干学生具有相同姓名或者相同成绩时,则按他们的学号递增排序。
*/ 
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
struct info{
	string no;
	string name;
	int grade;
};
//学号递增 
bool cmp1(info a, info b){
	return a.no < b.no;
} 
//姓名非递减
bool cmp2(info a, info b){
	if(a.name == b.name){
		return a.no < b.no;
	}
	return a.name < b.name;
} 
//成绩非递减排序
bool cmp3(info a, info b){
	if(a.grade == b.grade){
		return a.no < b.no;
	}
	return a.grade < b.grade;
} 
int main(){
	
	int n, c;
	while(cin>>n && n != 0){
		vector<info> infos;
		cin>>c;
		for(int i = 0; i < n; i++){
			info t;
			cin>>t.no>>t.name>>t.grade;
			infos.push_back(t);
		}
		if(c == 1){
			sort(infos.begin(), infos.end(), cmp1);
		}else if(c == 2){
			sort(infos.begin(), infos.end(), cmp2);
		}else {
			sort(infos.begin(), infos.end(), cmp3);
		}
		cout<<"Case:"<<endl;
		for(int i = 0; i < n; i++){
			cout<<infos[i].no<<" "<<infos[i].name+" "<<infos[i].grade<<endl;
			
		}
	}
	return 0;
}

字符串内排序(1360秒)

没什么

/*
输入一个字符串,长度小于等于200,然后将输出按字符顺序升序排序后的字符串。

*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
int main(){
	string s;
	while(cin>>s){
		sort(s.begin(), s.end());
		cout<<s<<endl; 
	}
	return 0;
}

华科排序(1399秒)

没啥

/*


输入的第一行包括一个整数n(1<=n<=100)。
    接下来的一行包括n个整数。

输出描述:

可能有多组测试数据,对于每组数据,将排序后的n个整数输出,每个数后面都有一个空格。
    每组测试数据的结果占一行。


*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
int main(){
	int n;
	while(cin>>n){
		vector<int> arr;
		for(int i = 0; i < n; i++){
			int t;
			cin>>t;
			arr.push_back(t);
		} 
		sort(arr.begin(), arr.end());
		for(int i = 0; i < n; i++){
			cout<<arr[i]<<" "; 
		}
		cout<<endl;
	}
	return 0;
} 

特殊排序(1400秒)

  • 使用while和pop_back(0将最后一个元素移出去
/*
可能有多组测试数据,对于每组数据,
第一行输出一个整数,代表N个整数中的最大值,并将此值从数组中去除,将剩下的数进行排序。
第二行将排序的结果输出。

*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
	int n;
	while(cin>>n) {
		vector<int> arr;
		for(int i = 0; i < n; i++) {
			int t;
			cin>>t;
			arr.push_back(t);
		}
		sort(arr.begin(), arr.end());
		int maxn = arr[arr.size()-1];
		arr.pop_back();
		while(arr.size() > 0 && arr[arr.size()-1] == maxn){
			arr.pop_back();
		}
		cout<<maxn<<endl; 
		if(arr.size() == 0) {
			cout<<-1<<endl;
		} else {
			for(int i = 0; i < arr.size(); i++) {
				cout<<arr[i]<<" ";
			}
			cout<<endl;
		}

	}
	return 0;
}

成绩排序(1404秒)

这几道华中的题都没啥技术含量了

/*
有N个学生的数据,将学生数据按成绩从低到高排序,
如果成绩相同则按姓名字符的字典序排序,
如果姓名的字典序也相同则按照学生的年龄从小到大排序,并输出N个学生排序后的信息。
输入输出格式
输入描述:

测试数据有多组,每组输入第一行有一个整数N(N<=1000),接下来的N行包括N个学生的数据。
每个学生的数据包括姓名(长度不超过100的字符串)、年龄(整形数)、成绩(小于等于100的正数)。

输出描述:

将学生信息按成绩进行排序,成绩相同的则按姓名的字母序进行排序。
然后输出学生信息,按照如下格式:
姓名 年龄 成绩

学生姓名的字母序区分字母的大小写,如A要比a的字母序靠前(因为A的ASC码比a的ASC码要小)。

输入输出样例
输入样例#:

3
abc 20 99
bcd 19 97
bed 20 97

输出样例#:
复制

bcd 19 97
bed 20 97
abc 20 99


*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
struct info{
	string name;
	int age;
	int grade;
}; 
bool cmp(info a, info b){
	if(a.grade == b.grade){
		if(a.name == b.name){
			return a.age < b.age;
		}
		return a.name < b.name;
	}
	return a.grade < b.grade;
}
int main(){
	int n;
	while(cin>>n){
		vector<info> infos;
		for(int i = 0; i < n; i++){
			info t;
			cin>>t.name>>t.age>>t.grade;
			infos.push_back(t);
		}
		sort(infos.begin(), infos.end(), cmp);
		for(int i = 0; i < n; i++){
			cout<<infos[i].name+" "<<infos[i].age<<" "<<infos[i].grade<<endl;
		}
	}
	return 0;
}

大整数排序(1412简单)

  • 大整数string接收,change转换,bign.data是顺位存放,高位存放在后面
  • 比较大整数,先判断长度
  • 输出,从高位开始输出
/*

输入描述:

输入第一行为一个整数N,(1<=N<=100)。
接下来的N行每行有一个数,数的长度范围为1<=len<=1000。
每个数都是一个正数,并且保证不包含前缀零。

输出描述:

可能有多组测试数据,对于每组数据,将给出的N个数从小到大进行排序,输出排序后的结果,每个数占一行。

输入输出样例
输入样例#:

3
11111111111111111111111111111
2222222222222222222222222222222222
33333333

输出样例#:
复制

33333333
11111111111111111111111111111
2222222222222222222222222222222222
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
struct bign{
	int data[1010];
	int len;
	bign(){
		memset(data, 0, sizeof(data));
		len = 0;
	}
};
bool cmp(bign a, bign b){
	if(a.len < b.len){
		return true;
	}else if(a.len > b.len){
		return false;
	}
	//位数相等
	for(int i = a.len-1; i >= 0; i--){
		if(a.data[i] < b.data[i]){
			return true;
		}
	} 
	return false;
	
} 
bign change(string s){
	bign a;
	int len = s.length(); 
	for(int i = 0; i < len; i++){
		a.data[len-i-1] = s[i]-'0';
	} 
	a.len = len;
	return a;
}
void bignout(bign a){
	for(int i = a.len-1; i >= 0; i--){
		cout<<a.data[i];
	}
	cout<<endl;
}
int main(){
	int n;
	while(cin>>n){
		vector<bign> bigns;
		for(int i = 0; i < n; i++){
			string sn;
			cin>>sn;
			bign tb = change(sn);
			bigns.push_back(tb);
		}
		sort(bigns.begin(), bigns.end(), cmp);
		for(int i = 0; i <n; i++){
			bignout(bigns[i]);
		}
	}
	return 0;
}

查找类问题

2022年3月25日09:47:54
补充体力√
2022年3月25日10:17:15

  • 查询次数很多,查询量很大的时候不能排序然后二分查找,太慢了
  • 使用map,底层是红黑树,查询和插入操作都是log级别

查找学生信息(1476简单)

  • 使用maps.find(key),返回键为key的迭代器,如果找不到则返回maps.end(),,否则可以直接[]访问
  • 字符串的映射只能用string 不能用char[]
  • 可以通过下标(key)访问,也可以通过迭代器map<type1, type2>::iterator it;
  • map会以key从小到大自动排序
  • maps.erase(it)
  • maps.erase(key)
  • maps.erase(sit, eit)
  • maps.clear()
  • map的键和值是一对一的关系
/*

输入描述:

输入的第一行为N,即学生的个数(N<=1000)
接下来的N行包括N个学生的信息,信息格式如下:
01 李江 男 21
02 刘唐 男 23
03 张军 男 19
04 王娜 女 19
然后输入一个M(M<=10000),接下来会有M行,代表M次查询,每行输入一个学号,格式如下:
02
03
01
04

输出描述:

输出M行,每行包括一个对应于查询的学生的信息。
如果没有对应的学生信息,则输出“No Answer!”

输入输出样例
输入样例#:

4
01 李江 男 21
02 刘唐 男 23
03 张军 男 19
04 王娜 女 19
5
02
03
01
04
03

输出样例#:
复制

02 刘唐 男 23
03 张军 男 19
01 李江 男 21
04 王娜 女 19
03 张军 男 19


*/
#include<cstdio>
#include<iostream>
#include<map>
using namespace std;
struct info{
	string no;
	string name;
	string sex;
	int age;
}; 
int main(){
	int n, m;
	while(cin>>n){
		map<string, info> maps;
		for(int i = 0; i < n; i++){
			info t;
			cin>>t.no>>t.name>>t.sex>>t.age;
			maps[t.no] = t;//将学号指向对应的结构体 
		}
		cin>>m;
		for(int i = 0; i < m; i++){
			string sno;
			cin>>sno;
			if(maps.find(sno) != maps.end()){
				cout<<maps[sno].no<<" "<<maps[sno].name<<" "<<maps[sno].sex<<" "<<maps[sno].age<<endl;
			}else{
				cout<<"No Answer!"<<endl;
			}
			
		}
	}
	return 0;
} 

动态查找(1477简单)

  • map的加入直接加maps[key] = xxx;
/*

输入描述:

第一行输入一个正整数n(n < 100000)
第二行输入n个正整数,用空格隔开。
第三行输入一个正整数q(q<100000),表示查询次数。
接下来输入q行,每行一个正整数x,查询x是否存在。

输出描述:

如果x存在,请输出find,如果不存在,请输出no,并将x加入到集合中。

输入输出样例
输入样例#:

5
1 2 3 4 5
3
6
6
3

输出样例#:
复制

no
find
find


*/
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<map>
using namespace std;
int main(){
	int n, m;
	while(cin>>n){
		map<int,int> maps;//key是数据,value是个数 
		for(int i = 0; i < n; i++){
			int x;
			cin>>x;
			maps[x]++;
		}
		cin>>m;
		for(int i = 0; i < m; i++){
			int s;
			cin>>s;
			if(maps.find(s) == maps.end()){
				cout<<"no"<<endl;
				maps[s]++;
			}else{
				cout<<"find"<<endl;
			}
		}
	}
	return 0;
} 

查找学生信息(1177简单)

北京大学上机

  • 使用俩map,其中一个也可以用数组或结构体更快一点,
  • 最后判断是否是悲剧的时候要-1
/*
把N个读者依次编号为1,2,…,N,把M本书依次编号为1,2,…,M。
和你喜欢读同一本书的人,就是你的潜在朋友。你现在的任务是从这份借阅记录中计算出每个人有几个潜在朋友。
输入输出格式
输入描述:

多组测试数据。
每个案例第一行两个整数N,M,2 <= N ,M<= 200。接下来有N行,第i(i = 1,2,…,N)行每一行有一个数,表示读者i-1最喜欢的图书的编号P(1<=P<=M)

输出描述:

每个案例包括N行,每行一个数,第i行的数表示读者i有几个潜在朋友。如果i和任何人都没有共同喜欢的书,则输出“BeiJu”(即悲剧,^ ^)

输入输出样例
输入样例#:

4 5
2
3
2
1

输出样例#:
复制

1
BeiJu
1
BeiJu


*/
#include<cstdio>
#include<map>
#include<iostream>
using namespace std;
int main(){
	
	int n, m;
	while(cin>>n){
		cin>>m;//
		map<int, int> maps;//编号,人数 
		map<int, int> people; 
		for(int i = 0; i < n; i++){
			int x;//喜欢的编号 
			cin>>x;
			people[i] = x;
			maps[x]++; 
		}
		for(int i = 0; i < n; i++){
			if(maps[people[i]]-1
			 == 0){
				cout<<"BeiJu\n";				
			}else{
				cout<<maps[people[i]] - 1<<endl;
			}
		}
	}
	return 0;
} 

再写一道吃饭

查找(1388秒)

毫无技术含量

/*
输入数组长度 n
输入数组      a[1...n]
输入查找个数m
输入查找数字b[1...m]
输出 YES or NO  查找有则YES 否则NO 。
输入输出格式
输入描述:

输入有多组数据。
每组输入n,然后输入n个整数,再输入m,然后再输入m个整数(1<=m<=n<=100)。

输出描述:

如果在n个数组中输出YES否则输出NO。

输入输出样例
输入样例#:

6
3 2 5 4 7 8
2
3 6

输出样例#:
复制

YES
NO


*/
#include<cstdio>
#include<iostream>
#include<map>
using namespace std;
int main() {
	int n, m;
	while(cin>>n) {
		map<int, int> maps;
		for(int i = 0; i < n; i++) {
			int x;
			cin>>x;
			maps[x]++;
		}
		cin>>m;
		for(int i = 0; i < m; i++) {
			int x;
			cin>>x;
			if(maps.find(x) == maps.end()) {
				cout<<"NO\n"; 
			}else{
				cout<<"YES\n";
			}
		}
	}
	return 0;
}

再写一道去吃饭

字符串反转替换(1387简单)

  • string的replace(sit, eit)
  • algorithm的reverse(sit, eit)
  • string的substr(si, ei)
/*
 命令格式:第一位0代表翻转,1代表替换;第二位代表待操作的字符串的起始下标int i;第三位表示需要操作的字符串长度int len。
输入输出格式
输入描述:

输入有多组数据。
每组输入一个字符串(不大于100)然后输入n,再输入n条指令(指令一定有效)。

输出描述:

根据指令对字符串操作后输出结果。

输入输出样例
输入样例#:

bac
2
003
112as

输出样例#:
复制

cab
cas


*/
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
void operate(string &s, string order){
	int index = order[1]-'0';
	int len = order[2]-'0';
	string sub = order.substr(3, index+len); 
	if(order[0] == '1'){//替换 
		s.replace(index, len, sub);
	}else{//反转 
		reverse(s.begin()+index, s.begin()+index+len);
	} 
}
int main(){
	string s;
	int n;
	while(cin>>s){
		cin>>n;
		for(int i = 0; i < n; i++){
			string order;
			cin>>order;
			operate(s, order);
			cout<<s<<endl;
			
		}
	}
	return 0;
} 

吃饭去2022年3月25日12:12:31
荞麦面+酸奶+海鲜包==yummy!
午觉【12:58, 13:18】洗衣服,洗鞋,now。14:13:54
写一道算法做英语

查找第k小的数(1383秒)

  • set可以自动去重排序,只是插入时insert,遍历时iterator,*it
/*

查找一个数组的第K小的数,注意同样大小算一样大。 如  2 1 3 4 5 2 第三小数为3。
输入输出格式
输入描述:

输入有多组数据。
每组输入n,然后输入n个整数(1<=n<=1000),再输入k。

输出描述:

输出第k小的整数。

输入输出样例
输入样例#:

6
2 1 3 5 2 2
3

输出样例#:
复制

3


*/
#include<cstdio>
#include<iostream>
#include<map>
#include<set>
using namespace std;
int main(){
	int n;
	while(cin>>n){
		set<int> st;
		for(int i = 0; i < n; i++){
			int x;
			cin>>x;
			st.insert(x);
		}
		int k;
		cin>>k;
		int i = 1;
		for(set<int>::iterator it = st.begin(); it != st.end(); it++){
			if(i == k){
				cout<<*it<<endl;
			}
			i++;
		}
	}
	return 0;
} 

再写一道贪心吧就写英语

贪心类问题

  • 主要还是看悟性,发现是贪心99%都能解决
  • 使用贪心时通常需要先排好序,搭配sort一起使用

喝饮料(简单)

  • 及时break出去
/*
商店里有n中饮料,第i种饮料有mi毫升,价格为wi。

小明现在手里有x元,他想吃尽量多的饮料,于是向你寻求帮助,怎么样买才能吃的最多。

请注意,每一种饮料都可以只买一部分。
输入输出格式
输入描述:

有多组测试数据。
第一行输入两个非负整数x和n。
接下来n行,每行输入两个整数,分别为mi和wi。
所有数据都不大于1000。
x和n都为-1时程序结束。

输出描述:

请输出小明最多能喝到多少毫升的饮料,结果保留三位小数。

输入输出样例
输入样例#:

233 6 
6 1
23 66
32 23
66 66
1 5
8 5
-1 -1

输出样例#:
复制

136.000


*/
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm> 
using namespace std;
struct drink{
	int mi;//毫升 
	int wi;//价格 
	double pi;//单价 
};
bool cmp(drink a, drink b){
	return a.pi<b.pi;
}
int main(){
	int x, n;//一共x元,n种饮料 
	while(cin>>x>>n){
		if(x == -1 && n == -1){
			break;
		}
		vector<drink> drinks;
		for(int i = 0; i < n; i++){
			drink t;
			cin>>t.mi>>t.wi;
			t.pi = (t.wi*1.0) / t.mi; 
			drinks.push_back(t);
		}
		sort(drinks.begin(), drinks.end(), cmp);
		double sum = 0;
		int i = 0;
		for(; i < n; i++){
			if(x >= drinks[i].wi){
				x-=drinks[i].wi;
				sum+=drinks[i].mi;
			}else{
				break;
			}
		}
		if(i < n){
			sum += (x*1.0)/drinks[i].pi; 
		}
		printf("%.3lf\n", sum);
	}
	return 0;
} 

写英语了14:58:13

2022年3月27日

昨天通宵,白天睡觉。。。今天继续,以后按时吃药

简单贪心

  • 及时break;
  • 最后要*1.0,最好用原始输入计算最后结果
  • 循环输入一定要注意每次输入完初始化,或者用局部变量呜呜
/*

n个板块,每个板块有w[i]个题目,需要消耗吴大佬m[i]的精力。一共能做多少道题目?
(没有a完就算成小数累加)

输入由多个测试用例组成,
每个测试用例是有两个非负整数m(总的精力),
n的行作为第一行,然后后面有n行跟随,每行包括两个非负整数w[i],m[i],最后一个测试用例后面有一组 -1 -1(所有的整数都不大于1000,毕竟人类是有极限的嘛hhh)
输出描述:
对于每一个测试用例,在一行中输出吴大佬可以做出的题目数目,精确到小数点后3位
输入输出样例
输入样例#:
复制
233 6 
6 1
23 66
32 23
66 66
1 5
8 5
-1 -1
输出样例#:
复制
136.000
*/ 

#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
struct node{
	int wi;//板块拥有的题目数 
	int mi;//板块需要的总精力
	double pi;//板块中每个题目需要的精力 
}; 
bool cmp(node a, node b){
	return a.pi < b.pi;
}
int main(){
	int m, n;//总的精力,板块个数 
	vector<node> nodes;
	while(cin>>m>>n){
		if(m == -1 && n == -1)break;
		for(int i = 0; i < n; i++){
			node t;
			cin>>t.wi>>t.mi;
			t.pi = (t.mi*1.0) / t.wi;
			nodes.push_back(t);
		} 
		sort(nodes.begin(), nodes.end(), cmp);
		
		double sum = 0;
		for(int i = 0; i < n; i++){
			if(m >= nodes[i].mi){
				sum += nodes[i].wi;
				m -= nodes[i].mi;
			}else{
				//最后半个
				sum += m*nodes[i].wi*1.0/nodes[i].mi;
				break;
			}
		}
		printf("%.3lf\n", sum);
		
	} 
	
	return 0;
} 

题目1347

只是觉得能力还不足,写了一下午,不过好歹自己完成全过程啊

  • 贪心策略
  • 边界问题
/*

*/
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
//每个加油站
struct node {
	double p;//油的单价
	double dis;//距离起点的距离
};
//按照距离从小到大排序
bool cmp1(node a, node b) {
	return a.dis < b.dis;
}
int main() {
	int cmax, d, davg, n;
	while(cin>>cmax>>d>>davg>>n) {
		vector<node> nodes;
		for(int i = 0; i < n; i++) {
			node t;
			cin>>t.p>>t.dis;
			nodes.push_back(t);
		}
		sort(nodes.begin(), nodes.end(), cmp1);//排序 
		//--------------走不到终点--------------------------------------------------------------- 
		if(nodes[0].dis != 0) { 
			cout<<"The maximum travel distance = 0.00\n";
			break;
		} 
		double fulld = cmax * davg;//加满油可以走的距离
		node nn;//终点设一个站 
		nn.dis = d;
		nn.p = 0;//价格一定是最小的,不然后面贪心算法到终点还会有剩余的油 
		nodes.push_back(nn);
		bool ok = false;//是否已经算出来,主要为了跳出下面的for循环之后跳出while循环 
		for(int i = 0 ; i < n; i++){
			if(nodes[i].dis + fulld < nodes[i+1].dis){
				printf("The maximum travel distance = %.2lf\n", nodes[i].dis + fulld);
				ok = true;
				break;
			}
		}
		if(ok){
			break;
		}
	
		 
		//-------------接下来一定可以到达终点------------------------------------------------------------- 
		//贪心策略:在加满油可以到达的车站集合中选择下一站nexti的位置
		//情况一: 集合中有一站的价格小于当前i站,则第一个小于的为nexti(先加油到该站,再选择下一站)
		//情况二:集合中所有站的价格都大于等于当前i站,则在该站就加满油,走到集合中价格最低的那一站(即过了这村就没这便宜店了)
		 
		double cost = 0;//总的花费 
		double nowt = 0;//当前的剩余 
		int i = 0;//当前站 
		//停留在当前站,开始思考怎样省钱........ 
		while(i < n){
			
			
			//找集合中价格比当前更低的第一个车站,否则是集合中价格最低的车站 
			int j = i+1;
			int nexti = j;//最后选择的下一站 
			bool flag = false;//集合中没有价格更低的车站 
			while(j <= n && nodes[i].dis+fulld >= nodes[j].dis){//在当前加满油的范围内 
				if(nodes[j].p < nodes[i].p){//情况一,第一个比当前价格低的车站 
					nexti = j;
					flag = true;
					break;
				}
				if(nodes[nexti].p > nodes[j].p){//情况二:同时找最低的车站 
					nexti = j;
				}
				j++;
			}
			if(flag == false){//接下来不停车了,现在加满油走到j 
				//加满油
				cost += (cmax-nowt)*nodes[i].p;
				nowt = cmax - (nodes[nexti].dis - nodes[i].dis)/davg;//走到j剩余油 
			}else{
				//别加满,加到走到nexti再加
				cost += ((nodes[nexti].dis-nodes[i].dis)/davg -nowt)*nodes[i].p;
				nowt = 0;
			} 
			i=nexti;
		} 
		printf("%.2lf\n", cost);	
	}
	return 0;
}

感觉还是手动模拟可以有贪心策略的灵感

2022年4月6日

——是自己来定义自己的生活,不要被无聊的无望牵制了生活。
——允许自己不完美,允许自己生气,脾气像辣椒一样火,允许自己焦虑,那是身体在提醒你该去跑跑步了,允许自己疲惫,这意味着你该闭上眼睛好好睡一觉了,允许自己尝试多次也不会成功,很容易办到的事情没什么意思。
在这里插入图片描述
在这里插入图片描述

  • 最短路径
  • 哈夫曼编码
  • 最小生成树
  • 突然想起来忘记做核酸,嘤,找寻规则的漏洞,看能不能逃过。

盛水最多的容器11(力扣)

  • 双指针
  • 贪心
class Solution {
public:
    int maxArea(vector<int>& height) {
        //双指针,可以遍历完所有可以的范围,
        //比暴力减少了一层时间复杂度
        int left = 0, right = height.size()-1;
        int ans = 0;
        while(left < right){
            int temp = min(height[left], height[right])*(right-left);
            ans = max(ans, temp);
            if(height[left] <= height[right]){
                left++;
            }else{
                right--;
            }
        }
        return ans;
    }
};

跳跃游戏55(力扣)

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int des = nums.size()-1;//最后的位置
        int farmost = nums[0];
        for(int i = 0; i <= des; i++){
            if(farmost >= des){
                return true;
            }
            if(farmost >= i){//能到达这里
                farmost = max(farmost, i+nums[i]);
            }
        }
        return false;
    }
};

跳跃游戏②(45)

class Solution {
public:
    int jump(vector<int>& nums) {
        int des = nums.size()-1;
        int time = 0;
        int farmost = 0;
        int end = 0;
        for(int i = 0; i < des; i++){
            //在于什么时候更新步数
            //我们维护当前能够到达的最大下标位置,记为边界。我们从左到右遍历数组,到达边界时,更新边界并将跳跃次数增加 1。
            //因为到达边界时,才能决定这次最远的位置
            farmost = max(farmost, i+nums[i]);
            if(end == i){
                time++;
                end = farmost;
            }

        }
        return time;
    }
};

2022年4月7日

买卖股票

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        //升序区间右-左
        int maxm = 0;
        int left = prices[0];
        int right = prices[0];
        for(int i = 0; i < prices.size(); i++){
            if(prices[i]>left){
                maxm += prices[i]-left;
            }
            left = prices[i];
        }
        return maxm;
    }

汽车加油

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int remain =0;
        int last = remain;
        int start = 0;
        //如果最后总last为负,那么一定不可以
        for(int i = 0; i < gas.size(); i++){
            last += gas[i]-cost[i];
            remain += gas[i]-cost[i];
            if(remain < 0){
            //如果当前reamin<0
            //那么从开始到当前i都不可以
                start = i+1;
                remain = 0;
            }
        }
        if(last < 0){
            return -1;
        }
        return start;
    }
};

分发糖果

class Solution {
public:
    int candy(vector<int>& ratings) {

        int n = ratings.size();
        vector<int> left(n);
        for (int i = 0; i < n; i++) {
            if (i > 0 && ratings[i] > ratings[i - 1]) {
                left[i] = left[i - 1] + 1;
            } else {
                left[i] = 1;
            }
        }
        int right = 0, ret = 0;
        for (int i = n - 1; i >= 0; i--) {
            if (i < n - 1 && ratings[i] > ratings[i + 1]) {
                right++;
            } else {
                right = 1;
            }
            ret += max(left[i], right);
        }
        return ret;
    }
};

最大值(179)

class Solution {
public:

    // bool cmp(string a, string b){
    //     return a+b > b+a;
    // }
    string largestNumber(vector<int>& nums) {
        vector<string> strnums;
        for(int i = 0; i < nums.size(); i++){
            strnums.push_back(to_string(nums[i]));
        }
        sort(strnums.begin(), strnums.end(), [](const string& a, const string& b){
            return a + b > b + a;
        });
        //连接
        string ans;
        int flag = 1;//是开头
        for(int i = 0; i < strnums.size(); i++){
            if(flag){
                if(strnums[i] == "0") 
                    continue;
                else{
                    flag = 0;
                }
            }
            ans += strnums[i];
        }
        return ans.size()?ans:"0";

    }
};

合并果子(哈夫曼树)

  • priority_queue《int》
  • greater《int》
  • pandua
#include<cstdio>
#include<queue>
#include<iostream> 
using namespace std;

/*
输入描述:
输入包括两行,第一行是一个整数n(1<=n<=10000),表示果子的种类数。
第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。
输出描述:
输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于2^31。
输入输出样例
输入样例#:
复制
3 
1 2 9
输出样例#:
复制
15
*/ 
int main(){
	int n;
	priority_queue<int, vector<int>, greater<int>> pq;//优先队列,小顶堆 
	scanf("%d", &n);
	for(int i = 0; i < n; i ++){
		int t;
		scanf("%d", &t);
		pq.push(t);
	}
	int ans = 0;
	while(pq.size() >= 2) {
		int a = pq.top();
		pq.pop();
		int b = pq.top();
		pq.pop();
		a += b;
		pq.push(a);
		ans+=a;
	}
	printf("%d\n", ans);
	return 0;
}

排对等待

  • pair的用法
  • map的key是固定的
#include<cstdio>
#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
bool cmp(pair<int, int>a, pair<int, int>b){
	return a.second < b.second;
}
int main(){
	int n;
	cin>>n;
	pair<int, int> times[1001];
	for(int i = 0; i < n; i++){
		
		int t;
		cin>>t;
		pair<int, int> temp_pair = pair<int, int>(i+1, t);
		times[i] = temp_pair;
	} 
	sort(times, times+n, cmp);
	long long sum = 0;
	long long temps = 0;
	for(int i = 0 ; i < n; i++){
		cout<<times[i].first<<" "; 
		sum += temps;
		temps += times[i].second;
	}
	cout<<endl;
	double ans = (sum*1.0)/n;
	printf("%.2lf", ans);
	return 0;
}

2022年4月9日

每日一题(数学780)

class Solution {
public:
    bool reachingPoints(int sx, int sy, int tx, int ty) {
        //先缩小tx,ty的值,直到某个与sx,sy相等
        while(tx > sx && ty > sy && tx != ty){
            if(tx > ty){
                tx = tx % ty;
            }else{
                ty = ty % tx;
            }
        }
        if(tx == sx && ty == sy){
            return true;
        }else if(tx == sx){
            return ty > sy && (ty-sy)%tx == 0;
        }else if(ty == sy){
            return tx > sx && (tx-sx)%ty==0;
        }else{
            return false;
        }
    }
};

通配符匹配(44用的动态规划)

  • 初始化数组
  • 状态转移方程
  • dp数组的定义和大小+1
  • vector二维数组的定义
class Solution {
public:
    bool isMatch(string s, string p) {
        int m = s.size();
        int n = p.size();
        vector<vector<int>> dp(m+1, vector<int>(n+1));
        dp[0][0] = true;
        //初始化dp[0][i]
        for(int i = 1; i <= n; i++){
            if(p[i-1] == '*'){
                dp[0][i] = true;
            }else{
                break;
            }
        }
        //动态规划
        for(int i = 1; i <= m; i++){
            for(int j = 1; j <= n; j++){
                if(p[j-1] == '*'){
                    dp[i][j] = dp[i][j-1] | dp[i-1][j];
                }else if(p[j-1] == '?' || p[j-1] == s[i-1]){
                    dp[i][j] = dp[i-1][j-1];
                }else{
                    dp[i][j] = false;
                }
            }
        }
        return dp[m][n];
    }
};

链表类问题

猴子报数(1081)

  • 循环链表的构建
  • 删除结点,最好在该结点判断好下一个该不该删,然后删除下一个
#include<cstdio>
#include<iostream>
using namespace std;
struct node{
	int num;
	struct node* next;
};
int n;
int s, m;//第s个开始,报数到m退出
//创建循环链表
node * create() {
	node* head, *now, *pre;
	for(int i = 1; i <= n; i++){
		now = (struct node*)malloc(sizeof(node));
		if(i == 1){
			head = now;
			pre = now;
		}
		now->num = i;
		now->next = head;
		pre->next = now;
		pre = now;
	}
	return head;
}
void print(struct node* head){
	struct node *p;
	p = head;
	s--;//走到第s个 
	while(s--){
		p = head->next;
	}
	int i = 1;
	while(p != NULL){
		if(p == p->next){
			cout<<p->num<<endl;
			break;
		}
		if((i+1)%m == 0){//如果s = 1  m = 3;到2的时候就判断,删除下一个
			printf("%d,", p->next->num);
			i = 1; 
			p->next = p->next->next;			
		}else{
			i++;
		}
		p = p->next;
		
	}
}
int main(){

	while(cin>>n>>s>>m){
		if(n == 0 && s == 0 && m == 0){
			break;
		}
		struct node *head = create();
		print(head);
	} 
	return 0;
}

单链表(1015)

  • 带头结点,头结点也要分配空间
#include<cstdio>
#include<iostream>
using namespace std;
typedef struct node{
	int num;
	struct node* next;
}node;
void insert(node *head, int k){
	//将t插入head的
	node *p = head;
	node *now = (node*)malloc(sizeof(node));
	now->num = k;		
	while(p->next != NULL){
		if(p->next->num > k){
			
			now->next = p->next;
			p->next = now;
			return;
			
		}else{
			p = p->next;
		}
	}
	now->next = p->next;
	p->next = now;

	
}

void print(node* head){
	while(head->next != NULL){
		cout<<head->next->num<< " ";
		head = head->next;
	}
}
int main(){
	node *head = (node*)malloc(sizeof(node));
	head->next = NULL;
	for(int i = 0; i < 5; i++){
		int t;
		cin>>t;
		insert(head, t);
	}
	print(head);
	return 0;
}

击鼓传花(循环链表)

  • 没有头结点需要pre,now,两个动态指针
#include<cstdio>
#include<iostream>
using namespace std;
typedef struct node{
	int num;
	struct node* next;
	
}node; 
node* create(int n){
	node* temp, *pre, *head;
	
	for(int i = 1; i <= n; i++){
		temp = (node*)malloc(sizeof(node));
		if(i == 1){
			head = temp;
			pre = temp;
		}
		temp->num = i;
		temp->next = head;
		pre->next = temp;
		pre = temp;
		
	}
	return head;
} 
void print(node* head){
	node * p = head;
	int i = 1; 
	while(p!=NULL){
		if(p->next == p){
			cout<<p->num;
			return;
		}
		if((i+1)%3 == 0){
			p->next = p->next->next;
			i=1;
			p = p->next;
		}else{
			i++; 
			p = p->next;
		}
	}
}
int main(){
	int n;//n个小朋友,从1开始编号
	while(cin>>n){
		node* head = NULL;
		head = create(n);
		print(head);//每次传3个 
		
	}
	 
	return 0;
}

增删改查

class MyLinkedList {
public:
    struct ListNode{
        int val;
        ListNode* next;
        ListNode(int x):val(x), next(nullptr){};
    };
    MyLinkedList() {
        _size = 0;
        _dummHead = new ListNode(0);
    }
    
    int get(int index) {
        if(index < 0 || index >= _size){
            return -1;
        }
        //举一个例子,一边模拟一边写
        ListNode* cur = _dummHead->next;
        while(index--){
            cur = cur->next;
        }
        return cur->val;
    }
    
    void addAtHead(int val) {
        ListNode* newNode = new ListNode(val);
        newNode->next = _dummHead->next;
        _dummHead->next = newNode;
        _size++;
    }
    
    void addAtTail(int val) {
        ListNode* cur = _dummHead;
        while(cur->next != nullptr){
            cur = cur->next;
        }
        ListNode* newNode = new ListNode(val);
        cur->next = newNode;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
        if(index < 0 || index > _size) return;
        ListNode* newNode = new ListNode(val);
        ListNode* cur = _dummHead;
        while(index--){
            cur = cur->next;
        }
        newNode->next = cur->next;
        cur->next = newNode;
        _size++;
    }
    
    void deleteAtIndex(int index) {
        if(index < 0 || index >= _size) return;
        ListNode* temp;
        ListNode* cur = _dummHead;
        while(index--){
            cur = cur->next;
        }
        temp = cur->next;
        cur->next = temp->next;
        delete temp;
        _size--;
    }

private:
    int _size;
    ListNode* _dummHead;
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

反转链表(双指针)

  • pre和cur一起走
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* cur = head;
        ListNode* pre = nullptr;
        ListNode* temp;//保存cur的下一个节点
        while(cur != nullptr){
            temp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
};

双指针(删除倒数第n个)

  • 用fast和slow指针
  • 删除都要提前一个元素就判断
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode();
        dummyHead->next = head;

        ListNode* fast = dummyHead;
        ListNode* slow = dummyHead;
        while(n-- && fast->next != nullptr){
            fast = fast->next;
        }
        //fast再走一步,为了让slow指向要删除的前一个
        fast = fast->next;
        while(fast != nullptr){
            slow = slow->next;
            fast = fast->next;
        }
        ListNode* temp = slow->next;
        slow->next = temp->next;
        delete temp;
        temp = dummyHead->next;
        delete dummyHead;
        return temp;
    }
};

环形链表(142力扣)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != nullptr && fast->next != nullptr){
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast){
                ListNode* index1 = head;
                ListNode* index2 = slow;
                while(index1 != index2){
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;//环的入口计算出来的
            }
        }
        return nullptr;
    }
};

链表的公共子序列的起点(02.07力扣)

  • 先让指向长的指针移动dislen
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* curA = headA;
        ListNode* curB = headB;
        int lenA = 0, lenB = 0;
        while(curA != nullptr){
            curA = curA->next;
            lenA++;
        }
        while(curB != nullptr){
            curB = curB->next;
            lenB++;
        }
        int dislen = 0;
        if(lenB > lenA){
            curA = headB;
            curB = headA;
            dislen = lenB-lenA;
        }else{
            curA = headA;
            curB = headB;
            dislen = lenA-lenB;
        }
        //现在curA指向的是较长的
        while(dislen--){
            curA = curA->next;
        }
        while(curA != nullptr){
            if(curA == curB){
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return nullptr;      
    }
};

2022年4月10日

每日一题(力扣哈希)

  • 有点简单
  • unordered_set更好点
class Solution {
public:
    int uniqueMorseRepresentations(vector<string>& words) {
         
    	set<string> res;
        vector<string> match = {".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."};
        for(int i = 0; i < words.size(); i++){
			string temp = words[i];
			string mtemp = "";
			for(int j = 0; j < temp.size(); j++){
				mtemp += match[temp[j]-'a'];
			}
			res.insert(mtemp);
		}
        return res.size();
    }
};

贪心

分发饼干

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int index = 0;
        for(int i = 0;i < s.size();++i){
            if(index < g.size() && g[index] <= s[i]){
                index++;
            }
        }
        return index;
    }
};

摆动序列

  • if逻辑
class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        //
        int curdiff = 0;//当前的差值
        int prediff = 0;//之前的差值
        int ans = 1;
        for(int i = 0; i < nums.size()-1; i++){
            curdiff = nums[i+1]-nums[i];
            if((curdiff > 0 && prediff<=0) || (curdiff < 0 && prediff >= 0)){
                ans++;
                prediff = curdiff;
            }
        }
        
        return ans;
    }
};

最大子序和

  • 初始化ans = INT32_MIN
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int ans = INT32_MIN;
        int temp = 0;
        for(int i = 0; i < nums.size(); i++){
            temp += nums[i];
            if(temp > ans){
                ans = temp;
            }
            if(temp < 0){
                temp = 0;
            }
        }
        return ans;
    }
};

跳跃游戏(55)

  • for循环在far的范围内
class Solution {
public:
    bool canJump(vector<int>& nums) {
        int des = nums.size()-1;
        int far = 0;
        for(int i = 0; i <= far; i++){
            if(far >= des) return true;
            far = max(far, i+nums[i]);
        }
        return false;
    }
};

跳跃游戏2

class Solution {
public:
    int jump(vector<int>& nums) {
        int nextdis = 0;
        int curdis = 0;
        int step = 0;
        for(int i = 0 ; i < nums.size()-1; i++){
            nextdis = max(nextdis, i+nums[i]);
            if(i == curdis){//移动到当前最远覆盖距离了,但是还没有到终点
                step++;
                curdis = nextdis;
            }
        }
        return step;
    }
};

反转

  • 绝对值从大到小排序
class Solution {
static bool cmp(int a, int b){
        return abs(a)>abs(b);
    }
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end(), cmp);
        int ans = 0;
        int n = nums.size();
        for(int i = 0; i < n;i++){
            if(k == 0){
                break;
            }
            if(nums[i] < 0){
                nums[i] = -nums[i];
                k--;
            }
        }
        while(k--){
            nums[n-1] = -nums[n-1];
        }
        for(int i = 0; i < nums.size(); i++){
            ans += nums[i];
        }
        return ans;
    }
};

柠檬水找零(860力扣)

class Solution {
public:
    bool lemonadeChange(vector<int>& bills) { 
        int count5 = 0;
        int count10 = 0;
        for(int i = 0 ; i < bills.size(); i++){
            if(bills[i] == 5){
                count5++;
            }else if(bills[i] == 10){
                if(count5 == 0){
                    return false;
                }else{
                    count5--;
                    count10++;
                }
            }else if(bills[i] == 20){
                if(count10>=1 && count5>=1){
                    count10--;
                    count5--;
                }else if(count5 >= 3){
                    count5-=3;
                }else{
                    return false;
                }
            }
        }
        return true;
    }
};

身高排序

  • 两个维度的先考虑一个维度,
  • insert的用法
  • sort排序是不稳定的
class Solution {
static bool cmp1(vector<int> a, vector<int> b){
    if(a[0] == b[0]){
        return a[1] < b[1];
    }//先按照身高相同的
    return a[0]> b[0];
}
public:
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        sort(people.begin(), people.end(), cmp1);
        vector<vector<int>> que;
        for(int i = 0 ; i < people.size(); i++){
            int pos = people[i][1];
            que.insert(que.begin()+pos, people[i]);
        }
        return que;
    }
};

2022年4月11日

温故

  • 删除链表元素while循环判断cur->next!=NULL
  • 删除元素都需要在当前判断next是否满足删除条件
  • 用虚拟头结点dummyHead = new node(val)
  • 设计链表,private:变量_size 和 _dummyHead
  • 反转链表,双指针cur,pre,需要一个temp在改变cur->next= pre之前保存cur->next
  • 两两交换相邻的结点,三步,最好画个图,循环判断next和next->next不为空,提前保存两个结点temp1 temp2保存
  • 删除倒数第n个,双指针法
  • 链表相交,先让curA跟curB末尾对齐,让curA先移动
  • 环形链表,使用快慢指针判断是否有环,如果有环,一个从相遇点开始,一个从头结点开始,知道相等,就是环的入口

合并链表1025(N诺)

  • 创建链表
  • 合并过程中需要一直更新指向next结点,
  • 使用虚拟头结点
#include<cstdio>
#include<iostream>
using namespace std;
/*

输入描述:

第一行输入第一个链表的结点数S1,S1<=100。
第二行输入S1个整数,两两之间用空格隔开。
第三行输入第二个链表的结点数S2,S2<=100。
第四行输入S2个整数,两两之间用空格隔开。

输出描述:

输出合并之后的链表结果,两两之间用空格隔开,末尾没有空格。

输入输出样例
输入样例#:

4
2 4 6 8
3
3 5 7

输出样例#:
复制

2 3 4 5 6 7 8

*/

typedef struct node {
	int val;
	node* next;
	node(int x):val(x), next(nullptr) {};
} node;
void insert(node* head, int k) {
	node *newNode = new node(k);
	if(head == nullptr) {

	}
}
node* Union12(node* h1, node* h2) {
	node* head = new node(0);
	node* cur1 = h1->next;
	node* cur2 = h2->next;
	node* cur3 = head;
	while(cur1 && cur2) {
		if(cur1->val < cur2->val) {
			cur3->next = cur1;
			cur1 = cur1->next;
		} else {
			cur3->next = cur2;
			cur2 = cur2->next;
		}
		cur3 = cur3->next;
	}
	while(cur1) {
		cur3->next = cur1;
		cur1 = cur1->next;
		cur3 = cur3->next;
	}
	while(cur2) {
		cur3->next = cur2;
		cur2 = cur2->next;
		cur3 = cur3->next;
	}
	return head;
}
int main() {
	int n1;
	cin>>n1;
	node* head1 = new node(0);
	node* cur1 = head1;
	for(int i = 0; i < n1; i++) {
		int t;
		cin>>t;
		node* temp = new node(t);
		cur1->next = temp;
		cur1 = cur1->next;
	}
	int n2;
	cin>>n2;
	node* head2 = new node(0);
	node* cur2 = head2;
	for(int i = 0; i < n2; i++) {
		int t;
		cin>>t;
		node* temp = new node(t);
		cur2->next = temp;
		cur2 = cur2->next;
	}

	node* head = Union12(head1, head2);
	head = head->next;
	while(head) {
		cout<<head->val<<" ";
		head = head->next;
	}


	return 0;
}

链表升序(1405)

  • 插入操作也是提前判断下一个是否符合条件,在下一个节点之前插入
/*

输入描述:

输入的每个案例中第一行包括1个整数:n(1<=n<=1000),接下来的一行包括n个整数。

输出描述:

可能有多组测试数据,对于每组数据,
将n个整数建立升序链表,之后遍历链表并输出。

输入输出样例
输入样例#:

4
9 7 5 3

输出样例#:
复制

3 5 7 9

*/
#include<cstdio>
#include<iostream>
using namespace std;
typedef struct node{
	int val;
	node* next;
	node(int x):val(x), next(nullptr){};
}node;
void insert(node* &head, int t){
	node* temp = new node(t);
	node* cur = head;
	while(cur->next){
		if(cur->next->val > t){
			temp->next = cur->next;
			cur->next = temp;
			return;
		}
		cur = cur->next;
	}
	cur->next = temp;
}
int main(){
	int n;
	while(cin>>n){
		node* head = new node(0);
		for(int i = 0 ; i < n; i++){
			int t;
			cin>>t; 
			insert(head, t);
		}
		node* cur = head->next;
		while(cur){
			cout<<cur->val<<" ";
			cur = cur->next;
		} 
	}
	return 0;
} 

每日一题(力扣)

  • 排列组合,阶乘。看懂题目
class Solution {
public:
    int countNumbersWithUniqueDigits(int n) {
        if(n == 0){
            return 1;
        }
        if(n == 1){
            return 10;
        }
        int ans = 10, t = 9;
        for(int i = 0; i < n-1; i++){
            t *= (9-i);
            ans+=t;
        }
        return ans;
    }
};

2022年4月12日

2022年4月12日22:45:26
忘记保存了呜呜,好朋友

2022年4月14日

2022年4月14日11:12:04
又学了一招,延迟满足,与自己的情绪脑和本能脑沟通:“该享受的一样都不会少,只不过不是现在,等事情先做完。”

每日一题,简单

质因数分解

  • 我是万万没想到
  • 思路应该就是不用考虑非质因数,因为该偶数的质因数已经先除过了。
#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    while(cin>>n){
        int cnt=0;
        for(int i=2;i*i<=n;i++){
            while(n%i==0){
                n/=i;
                cnt++;
            }
        }
        if(n>1) cnt++;
        cout<<cnt<<endl;
    }
    return 0;
}

反正骨子里的自信和谦逊都是无条件的

计数质数204(力扣)

-在n的范围内筛选,不要一上来就先无脑打表

class Solution {
public:
    int countPrimes(int n) {
        vector<bool> p(n, false);
        int ans = 0;
        for(int i = 2; i < n; i++){
            if(p[i] == false){
                ans++;
                for(int j = i + i; j < n; j += i){
                    p[j] = true;
                }
            }
        }
        return ans;
    }
};

质数排列(筛法,全排列,取模)

  • 也不要无脑<maxn,有时候是要<=的
  • 乘法取模可以用Longlong作为中间类型接收,少%几次
class Solution {
public:
    int numPrimeArrangements(int n) {
        long long ans = 1;
	int pnum = 0;
	vector<bool> p(n+1, false);
	for(int i = 2; i <= n; i++) {
		if(p[i] == false) {
			pnum++;
			for(int j = i + i; j <= n; j += i) {
				p[j] = true;
			}
		}
	}
	//计算pnum的阶乘和hnum的阶乘
	int d = 1000000007;
	for(int i = 1; i <= pnum; i++){
		ans *= i;
		ans %= d;
	}
	for(int i = 1; i <= n-pnum; i++){
		ans *= i;
		ans %= d;
	}
        return ans;
    }
};

1的个数

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int ans = 0;
        for(int i = 0; i < 32; i++){
            if(n & (1<<i)){
                ans++;
            }
        }
        return ans;
    }
};

762 二进制1的个数是质数的个数

  • 统计数字在二进制下“1”的个数 __builtin_popcount(i)
class Solution {
public:
bool isPrime(int x) {
        if (x < 2) {
            return false;
        }
        for (int i = 2; i * i <= x; ++i) {
            if (x % i == 0) {
                return false;
            }
        }
        return true;
    }

    int countPrimeSetBits(int left, int right) {
       
        //化为二进制   
        int ans = 0;
        for(int i = left; i <= right; i++){
            
            if(isPrime(__builtin_popcount(i))) ans++;            
        }
        return ans;
    }
};

二分快速幂

刚才打扫卫生的大叔当着我的面把我得一卷卫生纸装兜里了,然后我一脸懵,看着他,他问我“不是纸吧?”

  • 对于任意一个数 s,它的二进制代表了它可以由 2 的次幂的累加和来表示。
  • 所以对于任意一个 x^y,我们都可以将 y 分解成 2 的幂次的形式。 例如 5^13 = 5^8 * 5^4 * 5^1

幂次方

  • 其实也是二分法。
  • 在这里插入图片描述
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
int pow_mod(ll x, ll n, ll d) {
	if(n == 0) return 1;
	if(n & 1){
		return x * pow_mod(x, n-1, d) % d;
	}else{
		ll half = pow_mod(x, n/2, d);
		return half * half % d;
	}
}

int main() {
	ll x, n;
	while(cin>>x>>n) {
		cout<<pow_mod(x, n, 233333)<<endl;
	}
	return 0;
}

在这里插入图片描述

斐波那契

使用oeis找规律

#include<cstdio>
#include<iostream>
using namespace std;
/*
给你一串长度为n的全为0的字符串,你可以进行一个压缩操作,将两个相邻的0压缩成一个1。请问最多会有多少种组合出现?

例如n为3则有下面3种组合:

000

10

01
1 2 3 5
f[n] = f[n-1]+f[n-2]
*/
long long f[10005] = {0};


int main() {
	int n;

	while(cin>>n) {
		f[0] = f[1] = 1;
		for(int i = 2; i <= n; i++) {
			f[i] = f[i-1]+f[i-2];
			f[i] %= 2333333;
		}
		cout<<f[n]<<endl;
	}
	return 0;
}

2022年4月14日18:42:22
今天吃到蒸菜啦~对英语答案(但愿别错太多球球了)

2022年4月15日

2022年4月15日09:28:47
好喜欢小学弟嘿嘿诶嘿嘿嘿嘿

每日一题(递归迷你语法分析器)

  • 还是不是很懂递归的编写。
class Solution {
public:
    int index = 0;
    NestedInteger deserialize(string s) {
        if(s[index] == '['){
            index++;
            NestedInteger ni;
            while(s[index] != ']'){
                ni.add(deserialize(s));
                if(s[index] == ','){
                    index++;
                }
            }
            index++;
            return ni;
        }else{
            bool negative = false;
            if(s[index] == '-'){
                negative = true;
                index++;
            }
            int num = 0;
            while(index < s.size() && isdigit(s[index])){
                num = num*10 + s[index]-'0';
                index++;
            }
            if(negative){
                num *= -1;
            }
            return NestedInteger(num);
        }
    }
};

大数用java解

数据结构

括号匹配

#include<cstdio>
#include<iostream>
#include<string>
#include<stack>
using namespace std;
int main() {
	string s;
	while(cin>>s) {
		stack<char> stk;
		int i;
		for(i = 0; i < s.size(); i++) {
			if(s[i] == '[' || s[i] == '(') {
				stk.push(s[i]);
			} else {
				if(stk.empty()) {
					break;
				} else {
					char t = stk.top();

					if((s[i] ==']'&&t=='[') || (s[i] == ')'&& t=='(')) {
						stk.pop();
					} else {
						break;
					}
				}

			}
		}
		if(stk.empty()&& i == s.size()) {
			cout<<"YES"<<endl;
		} else {
			cout<<"NO"<<endl;
		}
	}
	return 0;
}

括号匹配

#include<cstdio>
#include<iostream>
#include<string>
#include<map>
#include<stack>
using namespace std;
int  main() {
	int n;
	map<char, char> mp;
	mp['['] = ']';
	mp['('] = ')';
	mp['{'] = '}';
	mp['<'] = '>';
	int pri[127] = {0};
	pri['<'] = 1;
	pri['('] = 2;
	pri['['] = 3;
	pri['{'] = 4;

	while(cin>>n) {

		while(n--) {


			stack <char> st;//定义栈
			string s;
			cin >> s;
			int flag = 0;
			for (int i = 0; i < s.length(); i++) {
				if(!st.empty()) { //如果栈不为空
					char t = st.top();
					if(mp.find(s[i]) != mp.end()&&pri[t]<pri[s[i]]) //如果括号优先级出错
						flag = 1;
					else if(mp[t] == s[i]) //如果括号匹配,出栈
						st.pop();
					else st.push(s[i]);//否则入栈
				} else st.push(s[i]); //栈为空时,入栈
			}
			if(st.empty()&& flag == 0)
				cout<<"YES"<<endl;
			else
				cout<<"NO"<<endl;
		}
	}
	return 0;
}

2022年4月15日14:41:20,做完第一道下午,晚上7点跟学弟一起去看电影嘿嘿,所以下午要高效率学习!!!
不能一直把头发

括号匹配(北京大学机试)

  • 需要两两遍扫描,第一遍从做到右,第二遍从右到左
  • 注意初始化数组为全空格
  • 只有一种括号,判断是否匹配直接判断是否为空即可
#include<cstdio>
#include<iostream>
#include<string>
#include<stack>
#include<vector> 
using namespace std;
int main(){
	string s;
	while(cin>>s){
		stack<char> stk;
		vector<char> arr(s.size(), ' ');
		//第一遍遍历字符串 找不能匹配的右括号
		for(int i = 0; i < s.size(); i++){
			if(s[i] == '('){
				stk.push(s[i]); 
				
			} else if(s[i] == ')'){
				if(stk.empty()){
					arr[i] = '?';
				}else{
					stk.pop();
					
				}
			}
		} 
		//第二遍遍历,找不能匹配的左括号
		while(!stk.empty()){
			stk.pop();
		} 
		for(int i = s.size()-1; i >= 0; i--){
			if(s[i] == ')'){
				stk.push(s[i]);
			}else if(s[i] == '('){
				if(stk.empty()){
					arr[i] = '$';
				}else{
					stk.pop();
				}
			}
		}
		cout<<s<<endl;
		for(int i = 0; i < s.size(); i++){
			cout<<arr[i];
		}
		cout<<endl;
	}
	return 0;
} 

哈夫曼树

合并果子

#include<cstdio>
#include<vector>
#include<iostream>
#include<queue>
using namespace std;
int main(){
	int n;
	while(cin>>n){
		priority_queue<int, vector<int>, greater<int>> nums;
		for(int i = 0; i < n; i++){
			int t;
			cin>>t;
			nums.push(t);
		}
		int ans = 0;
		while(nums.size() >= 2){
			int a = nums.top();
			nums.pop();
			int b = nums.top();
			nums.pop();
			a += b;
			nums.push(a);
			ans += a;//结果是累加的结点和 
		}
		if(n == 1){
			ans = nums.top();
			nums.pop();
		} 
		cout<<ans<<endl;
	} 
	return 0;
}

哈夫曼树1382

  • 和之前的代码一样,
  • 只不过含义成了叶子结点的权重*值的和
#include<cstdio>
#include<vector>
#include<iostream>
#include<queue>
using namespace std;
int main(){
	int n;
	while(cin>>n){
		priority_queue<int, vector<int>, greater<int>> nums;
		for(int i = 0; i < n; i++){
			int t;
			cin>>t;
			nums.push(t);
		}
		int ans = 0;
		while(nums.size() >= 2){
			int a = nums.top();
			nums.pop();
			int b = nums.top();
			nums.pop();
			a += b;
			nums.push(a);
			ans += a; 
		} 
		if(n == 1){
			ans = nums.top();
			nums.pop();
		}
		cout<<ans<<endl;
	}
	return 0;
} 

哈夫曼编码

  • 判断结点是否只有一个得提前判断
  • 归结到最后那个优先队列中最后也会剩下一个
#include<cstdio>
#include<vector>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int main() {
	string s;
	int num[40];
	while(cin>>s) {
		if(s == "END") {
			break;
		}
		memset(num, 0, sizeof(num));

		for(int i = 0; i < s.size(); i++) {
			if(s[i] >= '0' && s[i] <= '9') {
				num[s[i]-'0']++;
			} else if(s[i] == '_') {
				num[36]++;
			} else {
				num[s[i]-'A'+10]++;
			}
		}
		priority_queue<int, vector<int>, greater<int>> nums;
		for(int i =0; i <= 36; i++) {
			if(num[i] != 0) {
				nums.push(num[i]);
			}
		}
		int ans = 0;
		if(nums.size() == 1) {
			ans = nums.top();
			nums.pop();
		}
		while(nums.size() >= 2) {
			int a = nums.top();
			nums.pop();
			int b = nums.top();
			nums.pop();
			a += b;
			ans += a;
			nums.push(a);
		}

		int a = 8*s.size();
		double b = (a*1.0)/ans;
		printf("%d %d %.1lf\n", a, ans, b);
	}
	return 0;
}

二叉树1109建立,前中后遍历,计算叶子结点

  • 都是用的递归,根据二叉树递归的定义,一般都是先想根结点,然后给出递归结束的情况,再递归。
  • 能用语言描述出来递归直接照着说的递归
#include<cstdio>
#include<string>
#include<iostream>
using namespace std;
typedef struct node{
	char data;
	node* left;
	node* right;
	node(char c):data(c), left(nullptr), right(nullptr){};
}* tree, node;
void create(tree &t){//注意加& 
	char c;
	cin >>c;
	if(c == '0') t = nullptr;
	else{
		t = new node(c);
		create(t->left);
		create(t->right);
	}
} 
void preOrder(tree t){
	if(t != nullptr){
		cout<<t->data<<" ";
		preOrder(t->left);
		preOrder(t->right);
	}
} 
void postOrder(tree t){
	if(t != nullptr){
		postOrder(t->left);
		postOrder(t->right);
		cout<<t->data<<" ";
	}
}
void inOrder(tree t){
	if(t != nullptr){
		inOrder(t->left);
		cout<<t->data<<" ";
		inOrder(t->right);		
	}
} 
int leaf(tree t){
	if(t == nullptr){
		return 0;
	}
	if(t->left == nullptr && t->right == nullptr){
		return 1;
	}
	return leaf(t->left) + leaf(t->right);
}
int main(){
	tree t;
	create(t);//创建 
	preOrder(t);//先序遍历 
	cout<<endl;
	inOrder(t);//中序遍历 
	cout<<endl;
	postOrder(t);//后序遍历
	cout<<endl;
	int leafs = leaf(t); 
	cout<<leafs<<endl;
	return 0;
} 

二叉树遍历(多组输入构建二叉树1161)

  • 是使用了全局变量string s 和int len=0
  • 创建中先判断是否到字符串末尾
#include<bits/stdc++.h>
using namespace std;
typedef struct node {
	char data;
	struct node *left,*right;
	node(char c): data(c), left(nullptr), right(nullptr){};
}*t;

string s;
int len;
void creatTree(t &T) {
	if(len == s.size())
		return;
	char c = s[len++];
	if(c == '#') T == nullptr;
	else {
		T = new node(c);
		
		creatTree(T->left);
		creatTree(T->right);
	}
}
void Midorder(t T) {

	if(T != nullptr) {
		Midorder(T->left);
		cout<<T->data<<' ';
		Midorder(T->right);
	}
}
int main() {
	while(cin >> s) {
		len = 0;
		t Tree;
		creatTree(Tree);
		Midorder(Tree);
		printf("\n");
		//return 0;
	}
	return 0;
}

二叉树中公共父节点(数组形式规律1233)

  • 从1开始的话,每个数的父节点是该数整除2(向下取整)

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
int main(){
	int x, y;
	while(cin>>x>>y){
		while(x != y){
			if(x > y){
				x/=2;
			}else{
				y/=2;
			}
		}
		cout<<x<<endl;
	} 
	return 0;
}

二叉树2(子树结点个数)

  • 递归,主要是递归结束条件,一个等于一个大于
  • 左子树是2倍,右子树是2倍+1
#include<bits/stdc++.h>
using namespace std;
int subnode(int m, int n){
	if(m == n){
		return 1;
	}
	if(m > n){
		return 0;
	}
	return subnode(2*m, n)+subnode(2*m+1, n) + 1;
}
int main(){
	int m, n;
	while(cin>>m>>n){
		int ans = subnode(m, n);
		cout<<ans<<endl;
	} 
	return 0;
}

前+中输出后序遍历(1401)

  • 真的不用构造二叉树绝绝子
#include<bits/stdc++.h>
using namespace std;
/*

输入描述:

两个字符串,其长度n均小于等于26。
第一行为前序遍历,第二行为中序遍历。
二叉树中的结点名称以大写字母表示:A,B,C....最多26个结点。

输出描述:

输入样例可能有多组,对于每组测试样例,
输出一行,为后序遍历的字符串。

输入样例#:

ABC
BAC
FDXEAG
XDEFAG

输出样例#:
复制

BCA
XEDGAF



*/


void postOrder(string pre, string mid){
	if(pre.size() == 0){
		return;
	}
	//FDXEAG  XDEFAG
	//DXE AG  XDE AG 
	char root = pre[0];
	int index = mid.find(root);
	string preleft = pre.substr(1, index);
	string preright = pre.substr(index+1);
	string midleft = mid.substr(0, index);
	string midright = mid.substr(index+1);
	postOrder(preleft, midleft);
	postOrder(preright, midright);
	cout<<root;
}

int main(){
	string s1, s2;
	while(cin>>s1>>s2){
		postOrder(s1, s2);
		cout<<endl;
	}
	return 0;
} 

2022年4月16日

2022年4月16日08:09:32

1551判断是否是对称二叉树

  • 层序遍历输入,判断是否是对称二叉树,
  • 递归,每次都是判断左子树的左边和右子树的右边开始向中间走
  • 层序输入的话,根结点是0,那么左子树下标2i+1 ,右子树下标2i+2
#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
/*
层次遍历的方式输入一个二叉树,判断这个二叉树的结构(即不用管结点的值)是否镜像对称。
输入输出格式
输入描述:

输入一行字母,其中#表示空节点(字母长度小于1000)。

输出描述:

如果输入的二叉树对称,输出YES,否则输出NO。

输入输出样例
输入样例#:

ABC####

输出样例#:
复制

YES


*/ 
bool judge(int left, int right, string s){
	if(right >= s.size()) return true;//判断完了,都满足要求 
	for(int i = left, j = right; i <= j; i++, j--){
		if((s[i] == '#'&& s[j] != '#') || (s[i] != '#' && s[j] == '#')){
			return false;
		}
	}
	return judge(2*left+1, 2*right+2, s);
}
int main(){
	string s;
	while(cin>>s){
		bool flag = judge(0, 0, s);
		if(flag){
			cout<<"YES"<<endl;
		}else{
			cout<<"NO"<<endl;
		}
	} 
	return 0;
} 

1561前序中序输出后序

  • 使用的一个函数,不构造二叉树
  • 注意下标
  • 还有提前判断是否到终点
#include<bits/stdc++.h>
using namespace std;
/*

输入描述:

第一行为二叉树先序遍历结果。
第二行为二叉树中序遍历结果。

输出描述:

二叉树后续遍历结果

输入输出样例
输入样例#:

426315
623415

输出样例#:
复制

632514
*/ 
void postOrder(string pre, string mid){
	if(pre.size() == 0) return;//判断到叶子结点 
	char root = pre[0];//根结点
	int index = mid.find(root);//找到根结点在中序遍历中的下标
	string preleft = pre.substr(1, index);//第二个参数是len  左子树 
	string preright = pre.substr(index+1); //右子树   
	string midleft = mid.substr(0, index);//左子树
	string midright = mid.substr(index+1);//右子树
	
	postOrder(preleft, midleft);
	postOrder(preright, midright);
	cout<<root; 
	 
}
int main(){
	string pre, mid;
	while(cin>>pre>>mid){
		postOrder(pre, mid);
		cout<<endl;
	}	
	return 0;
}

二叉排序树

  • 定义:空树
  • 若左子树不为空,左子树上所有结点的值都小于根结点的值
  • 若右子树不为空,右子树上所有结点的值都大于根节点的值
  • 左右子树都是二叉排序树
  • 所有结点的值都不相同
  • 有点像map
  • 一般问题考察定义:建立二叉排序树,输出前中后遍历

二叉排序树

  • 插入创建二叉树
  • 插入更新传入的参数都需要用引用&
  • 遍历的时候在不为nullptr的if语句里面就可以了,不用写return 了。
#include<bits/stdc++.h>
using namespace std;
/*
输入一系列整数,建立二叉排序树,并进行前序,中序,后序遍历。
输入输出格式
输入描述:

输入第一行包括一个整数n(1<=n<=100)。
接下来的一行包括n个整数。

输出描述:

可能有多组测试数据,对于每组数据,将题目所给数据建立一个二叉排序树,并对二叉排序树进行前序、中序和后序遍历。
每种遍历结果输出一行。每行最后一个数据之后有一个空格。
输入中可能有重复元素,但是输出的二叉树遍历序列中重复元素不用输出。

输入输出样例
输入样例#:

5
1 6 5 9 8

输出样例#:
复制

1 6 5 9 8
1 5 6 8 9
5 8 9 6 1
*/
typedef struct node {
	int data;
	node* left, *right;
	node(int x):data(x), left(nullptr), right(nullptr) {};
}*tree, node;
void insert(tree &t, int c) {
	if(t == nullptr) { //空树
		node *newNode = new node(c);//树空的时候才建立新结点插入
		t = newNode;
		return;
	}
	if(c == t->data) {
		return ;//所有元素都相同
	}
	if(c < t->data) {
		return insert(t->left, c);
	} else {
		return insert(t->right, c);
	}
}
void preOrder(tree t) {
	if(t != nullptr) {
		cout<<t->data<<" ";
		preOrder(t->left);
		preOrder(t->right);
	}
}
void midOrder(tree t) {
	if(t != nullptr){
		midOrder(t->left);
		cout<<t->data<<" ";
		midOrder(t->right);
	}
}
void postOrder(tree t){
	if(t != nullptr){
		postOrder(t->left);
		postOrder(t->right);
		cout<<t->data<<" ";
	}
}
int main() {
	int n;
	while(cin>>n) {
		tree t = nullptr;
		for(int i = 0; i < n; i++) {
			int c;
			cin>>c;
			insert(t, c);
		}
		preOrder(t);
		cout<<endl;
		midOrder(t);
		cout<<endl;
		postOrder(t);
		cout<<endl;
	}
	return 0;
}

哈希数组

  • 其实大部分用map都能解决,
  • 主要针对小范围的
#include<bits/stdc++.h>
using namespace std;
/*
读入N名学生的成绩,将获得某一给定分数的学生人数输出。

测试输入包含若干测试用例,每个测试用例的格式为


第1行:N
第2行:N名学生的成绩,相邻两数字用一个空格间隔。
第3行:给定分数

当读到N=0时输入结束。其中N不超过1000,成绩分数为(包含)0到100之间的一个整数。

输出描述:

对每个测试用例,将获得给定分数的学生人数输出。

输入输出样例
输入样例#:

3
80 60 90
60
2
85 66
0
5
60 75 90 55 75
75
0

输出样例#:
复制

1
0
2


*/

int main(){
	int n;
	while(cin>>n){
		if(n == 0)break;
		int t;
		map<int, int> mp;
		for(int i = 0; i < n; i++){
			cin>>t;
			mp[t]++;
		}
		int search;
		cin>>search;
		if(mp.find(search) == mp.end()){
			cout<<'0'<<endl;
		}else{
			cout<<mp[search]<<endl;
		}
	} 
	return 0;
}

1225 map潜在朋友

  • 这个需要一个map存储图书的读者数量
  • 一个vector存储读者
#include<bits/stdc++.h>
using namespace std;
/*
把N个读者依次编号为1,2,…,N,把M本书依次编号为1,2,…,M。同时,按照“臭味相投”的原则,和你喜欢读同一本书的人,就是你的潜在朋友。你现在的任务是从这份借阅记录中计算出每个人有几个潜在朋友。
输入输出格式
输入描述:

每个案例第一行两个整数N,M,2 <= N ,M<= 200。
接下来有N行,第i(i = 1,2,…,N)行每一行有一个数,
表示读者i-1最喜欢的图书的编号P(1<=P<=M)

输出描述:

每个案例包括N行,每行一个数,第i行的数表示读者i有几个潜在朋友。
如果i和任何人都没有共同喜欢的书,则输出“BeiJu”(即悲剧,^ ^)

输入输出样例
输入样例#:

4  5
2
3
2
1

输出样例#:
复制

1
BeiJu
1
BeiJu


*/
int main(){
	int n, m;//图书编号m:1-m   //一共n个人
	while(cin>>n>>m){
		map<int, int> mp;//first:图书编号    second:读的人数 
		vector<int> peo;
		for(int i = 0; i < n; i ++){
			int t;
			cin>>t;
			peo.push_back(t);
			mp[t]++;
		}
		for(int i = 0; i < n; i++){
			if(mp[peo[i]] == 1){
				cout<<"BeiJu"<<endl; 
			}else{
				cout<<mp[peo[i]]-1<<endl;
			}
		}
	} 
	return 0;
}

区间覆盖1175

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n, m;//树的数量,区间个数 
	while(cin>>n>>m){
		vector<int> nums(n+1, 0);
		for(int i = 0; i < m; i++){
			int a, b;
			cin>>a>>b;
			for(int j = a; j <= b; j++){
				nums[j]++;
			} 
		} 
		int count = 0;
		for(int i = 0; i <= n; i ++){
			if(nums[i] == 0){
				count++;
			}
		}
		cout<<count<<endl;
	}
	return 0;
}

又崩了N诺

刷墙1209

  • 时间复杂度
  • 用scnaf输入
  • 只标记首尾可以降低一些时间复杂度
#include<bits/stdc++.h>
using namespace std;
/*

输入描述:

若干行输入,每行两个数字B[i],E[i](0<=B[i]<=E[i]<=200000)表示这次刷的墙壁是哪一段
(假设每次刷的时候油漆颜色都和之前的不同),以0 0结束
又若干行输入,每行两个数字begin[i],end[i](0<=begin[i]<=end[i]<=200000)表示小诺询问的段,
以0 0结束

输出描述:

对于每个小诺的询问输出(end[i]-begin[i]+1)行,表示对应询问段的每个点被多少种颜色的油漆覆盖过。

输入输出样例
输入样例#:

1 20
5 10
10 20
0 0
4 6
10 11
0 0

输出样例#:
复制

1
2
2
3
2


*/
const static int maxn = 200005;
int h[maxn];
int main() {
	int b, e;
	//刷墙
	while(scanf("%d%d", &b, &e)) {
		if(b == 0 && e == 0) {
			//memset(h, 0, sizeof(h));
			break;
		}

		h[b]++;
		h[e+1]--;
	}
	for(int i = 1; i < maxn; i++){
		h[i] += h[i-1];
	}
	while(scanf("%d%d", &b, &e)) {
		if(b == 0 && e == 0) {
			break;
		}
		for(int i = b; i <= e; i++) {
			printf("%d\n", h[i]);

		}
	
	}

	return 0;
}

2022年4月17日

2022年4月17日08:56:00

前缀字符串1098

  • 说的是使用前缀树,map
  • 好像排序更简单
#include<cstdio>
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
int main(){
	int n;
	while(cin>>n){
		if(n == 0) break;
		vector<string> ss;
		string ts;
		for(int i = 0; i < n; i++){
			cin>>ts;
			ss.push_back(ts);
		}
		sort(ss.begin(), ss.end());
		int count = 0;
		for(int i = 1; i < n; i++){
			if(ss[i].find(ss[i-1]) != 0){//string.find()  返回的是下标   找不到返回string::npos   为-1
			 	count++;
			}
		}
		cout<<count+1<<endl;//最后一个肯定是
	}
	return 0;
}

搜索

暴力枚举-白鸡问题1348

#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
/*
用小于等于n元去买100只鸡,大鸡5元/只,小鸡3元/只,还有1/3元每只的一种小鸡,分别记为x只,y只,z只。编程求解x,y,z所有可能解。
输入输出格式
输入描述:

测试数据有多组,输入n。

输出描述:

对于每组输入,请输出x,y,z所有可行解,按照x,y,z依次增大的顺序输出。
*/
int main(){
	
	int n;
	while(cin>>n){
		int x, y, z;
		for(x = 0; x <= 100; x++){
			for(y = 0; y <= 100-x; y++){
				z = 100 - x- y;
				double temp = 5*x+3*y+1.0/3*z;
				if(temp <= n){
					printf("x=%d,y=%d,z=%d\n", x, y, z);
				}
			}
		}
	}
} 

暴力枚举-abc1165

  • 只是注意首位不为0
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
/*
设a、b、c均是0到9之间的数字,abc、bcc是两个三位数,且有:abc+bcc=532。求满足条件的所有a、b、c的值。
*/
int main(){
	int a, b, c;
	for(a = 1; a <= 9; a++){
		for(b = 1; b <= 9; b++){
			for(c = 0; c <= 9; c++){
				int x = a*100+b*10+c;
				int y = b*100+c*10+c;
				if(x+y == 532){
					printf("%d %d %d\n", a, b, c);
				}
			}
		} 
	}
	return 0;
}

暴力枚举-火鸡的价格1274

#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
	
	int n;//火鸡的数量
	while(cin>>n){
		int x, y, z;
		cin>>x>>y>>z;
		bool flag = false;
		int t = x*1000+y*100+z*10;
		int a, b, price = 0;
		for(a = 9; a >=  1; a--){
			for(b = 9; b >= 0; b--){
				int sum = a*10000+t+b;
				if(sum % n == 0){
					printf("%d %d %d\n", a, b, sum/n);
					flag = true;
					break;
				}
			}
			if(flag)break;
		}
		if(flag == false){
			cout<<'0'<<endl;
		}
	} 
	return 0;
}

BFS

  • 一层一层遍历
  • 使用队列

1563-BFS走迷宫

  • 定义结点结构体
  • 定义迷宫数组
  • 定义访问标记数组
  • 定义方向数组
  • 使队列
  • 结构体可以直接构建node{1, 2, 3};
#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;

/*

输入描述:

有多组测试数据。
第一行输入两个正整数 H(0 < H <= 100)和 W(0 < W <= 100),分别表示迷
宫的高和宽。
接下来 H 行,每行 W 个字符(其中‘*’表示路,‘#’表示墙,‘S’表示小 A
的位置,‘E’表示迷宫出口)。
当 H 与 W 都等于 0 时程序结束。

输出描述:

输出小 A 走到迷宫出口最少需要花费多少秒,如果永远无法走到出口则输出“-1”。

输入输出样例
输入样例#:

3 3
S*#
**#
#*E
0 0

输出样例#:
复制

4


*/
const int maxn = 105;
typedef struct node{
	int x, y;
	int step;
}node;
int sx, sy;
char maze[maxn][maxn];
int vis[maxn][maxn];
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};//右下左上 

int bfs(int sx, int sy){
	memset(vis, 0, sizeof(vis));
	queue<node> q;
	q.push(node{sx, sy, 0});
	vis[sx][sy] = 1;
	int ans = -1;
	while(!q.empty()){
		node now = q.front();
		q.pop();
		if(maze[now.x][now.y] == 'E'){
			ans = now.step;
			break;
		}
		for(int i = 0; i < 4; i++){
			int nx = now.x + dir[i][0];
			int ny = now.y + dir[i][1];
			if(vis[nx][ny] == 0 && (maze[nx][ny] == '*' || maze[nx][ny] == 'E')){
				q.push(node{nx, ny, now.step+1});
				vis[nx][ny] = 1;
				
			}
		}
	}
	return ans;
}
int main(){
	int h, w;
	while(cin>>h>>w){
		if(h == 0 && w == 0){
			break;
		}
		memset(maze, 0, sizeof(maze));	//在cstring里面 
		for(int i = 1; i <= h; i++){
			scanf("%s", maze[i]+1);
			for(int j = 1; j <= w; j++){
				if(maze[i][j] == 'S'){
					sx = i;
					sy = j;
				}
			}
		}
		
		int ans = bfs(sx, sy);
		printf("%d\n", ans);
		
	}
	return 0;
}

2022年4月18日

2022年4月18日08:59:39
狠狠地刷算法吧

递归

汉诺塔

  • hanoi(n, a, b, c)需要四个参数
  • move(a, c)两个参数
#include<cstdio>
#include<iostream>
using namespace std;
int step = 0;
void move(char a, char c){
	printf("%c-->%c   ", a, c);
	step++;
	if(step % 5 == 0){
		printf("\n");
		step = 0;
	} 
}
void hanoi(int n, char a, char b, char c){
	//借助b将a移动到c
	if(n == 1){
		move(a, c);
	} else{
		hanoi(n-1, a, c, b);
		move(a, c);
		hanoi(n-1, b, a, c);
	}
}
int main(){
	int n;
	while(cin>>n){
		if(n == 0) break;
		step = 0;
		hanoi(n, 'A', 'B', 'C');
		cout<<endl;
	}	
	return 0;
} 

1185全排列

#include<cstdio>
#include<string>
#include<algorithm>
#include<iostream>
using namespace std;
int main(){
	string s;
	while(cin>>s) {
		sort(s.begin(), s.end());
		do{
			cout<<s<<endl;
		}while(next_permutation(s.begin(), s.end()));
	}
	return 0;
}

DFS

  • 深度优先+栈+递归(递归是DFS的一种实现方式)
  • 走迷宫引入
  • 深度优先会枚举完所有的路径
  • 用递归实现深度优先
  • 分析问题,抽象出岔道口、死胡同
  • 定义一个栈
  • 以深度为关键词访问这些岔道口和死胡同,并将他们入栈
  • 离开岔道口和死胡同的时候将他们出栈
  • 求斐波那契数列也可以抽象
  • 0-1背包问题,放与不放是岔路口,index==n是死胡同
  • 在进入放的岔路口前先判断是否可以剪枝
  • 0-1背包问题----->枚举出来所有的子序列,选择一个最优子序列
  • 枚举从N个数中选取K个数的所有方案

油田储存DFS

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
using namespace std;
const int maxn = 105;
int vis[maxn][maxn];
char maap[maxn][maxn]; 
int dir[8][2] = {1,0,0,-1,-1,0,0,1,1,1,1,-1,-1,1,-1,-1};
void dfs(int i, int j){
	vis[i][j] = 1;
	for(int t = 0 ; t < 8; t++){
		int nx = i+dir[t][0];
		int ny = j+dir[t][1];
		if(maap[nx][ny] == '@' && vis[nx][ny] == 0){
			dfs(nx, ny);//有一种粘连的感觉
		}
	}
}
int main(){
	int m, n;//行,列
	while(cin>>m>>n){
		if(m == 0 && n == 0){
			break;
		}
		memset(vis, 0, sizeof(vis));
		memset(maap, 0, sizeof(maap));
		for(int i = 1; i <= m; i++){
			scanf("%s", maap[i]+1);
		}
		int ans = 0;
		for(int i = 1; i <= m; i++){
			for(int j = 1; j <= n; j++){
				if(maap[i][j] == '@' && vis[i][j] == 0){
					ans++;
					dfs(i, j);	
				} 
			}
		}
		cout<<ans<<endl;
	} 
	return 0;
}

图论

  • 对于大部分图论问题,直接套用算法模板就行
  • 并查集、最小生成树、最短路径、拓扑排序
  • 连通图,任意两个顶点之间存在路径
  • 连通分量,一个图的最大连通子图(连通的前提下不能包含更多顶点)
  • 邻接表,边数较少用
  • 邻接矩阵,边数较多方便,求出入度,边数

并查集路径优化

  • 朋友关系、道路连通
  • 使用递归的形式比递推更简洁一些
    在这里插入图片描述

畅通工程2 1319

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn = 1005;
int father[maxn];

int find(int x){
	if(x == father[x]) return x;//找到根
	return father[x] =find(father[x]);
} 
int main(){
	int n, m;//n个城镇[1, n],m条道路 
	while(cin>>n){
		
		if(n == 0) break;
		cin>>m;
		//初始化并查集
		for(int i = 1; i <= n; i++) father[i] = i;//n个独立的集合
		int ans = 0; 
		for(int i = 0; i < m; i++){
			int a, b;
			cin>>a>>b;
			int fa = find(a);
			int fb = find(b);
			if(fa != fb){
				father[a] = fb; 
			} 
		}
		for(int i = 1; i <= n; i++){
			if(father[i] == i) ans++;
		}
		cout<<ans-1<<endl;//因为那个连通要建的边比孤立顶点数少1 
	} 
	return 0;
}

最小生成树(权值最小的树)

  • kruskal算法(排序、使用并查集)、prim算法
  • 判断是否连通,最后边数==顶点数-1,就是生成了最小生成树

畅通工程1312

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector> 
using namespace std;
typedef struct node{
	int u, v, w;
}node;
bool cmp(node a, node b){
	return a.w < b.w;
}

const int maxn = 105;
int father[maxn];
int find(int x){
	if(father[x] == x) return x;
	return father[x] = find(father[x]);
}

int main(){
	int n, m;//道路条数,村庄数目[1, m]
	while(cin>>n){
		if(n == 0){
			break;
		}
		cin>>m;
		vector<node> roads;
		for(int i = 0; i < n; i++){
			node temp;
			cin>>temp.u>>temp.v>>temp.w;
			roads.push_back(temp);
		}
		//排序
		sort(roads.begin(), roads.end(), cmp);
		//初始化并查集
		for(int i = 1; i <= m; i++){
			father[i] = i;
		} 
		int totalroad = 0, sumw = 0;
		//合并的过程 
		for(int i = 0; i < n; i++){//遍历每条路 
			int a = find(roads[i].u);
			int b = find(roads[i].v);
			if(a != b){
				totalroad++;
				sumw+=roads[i].w;
				father[a] = b;//合并 
			}
		}
		if(totalroad == m-1){//边数等于顶点数-1,说明连通
			cout<<sumw<<endl; 
		}else{
			cout<<"?"<<endl;
		}
	} 
	return 0;
} 

畅通工程,最小生成树(变1311)

  • 并查过程中 不要忘记合并,
  • 初始化并查集是初始化的顶点,要是1开始
  • 并查过程是遍历的边,然后检查边的端点
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
//有一些已经有路
typedef struct node{
	int u, v, w, s;
}node; 
bool cmp(node a, node b){
	return a.w < b.w;
}
//
const int maxn = 105;
int father[maxn]; 
int find_father(int x){
	if(father[x] == x) return x;
	return father[x] = find_father(father[x]);
}
int main(){
	int n;//村庄[1, n];
	while(cin>>n){
		if(n == 0) break;
		int roadnum = n*(n-1)/2;
		vector<node> roads; 
		for(int i = 0; i < roadnum; i++){
			node t;
			cin>>t.u>>t.v>>t.w>>t.s;
			if(t.s == 1) t.w = 0;
			roads.push_back(t);
		} 
		sort(roads.begin(), roads.end(), cmp);
		//初始化并查集
		for(int i = 1; i <= n; i++) father[i] = i;
		//
		int sumw = 0;
		//并查
		for(int i = 0; i < roadnum; i++){
			int a = find_father(roads[i].u);
			int b = find_father(roads[i].v);
			if(a != b){
				sumw+=roads[i].w;
				father[a] = b;
			}
		} 
		cout<<sumw<<endl;
	}
	return 0;
}

1341还是畅通工程(kruskal最小生成树)

  • 好像只是练手
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef struct node{
	int u, v, w;
}node;
bool cmp(node a, node b){
	return a.w < b.w;
}
//
const int maxn = 105;
int father[maxn];
int find_father(int x){
	if(father[x] == x) return x;
	return father[x] = find_father(father[x]);
}
int main(){
	int n;//村庄数目
	while(cin>>n){
		if(n == 0) break;
		int m = n*(n-1)/2;//道路总数
		vector<node> roads;
		for(int i = 0; i < m; i++){
			node temp;
			cin>>temp.u>>temp.v>>temp.w;
			roads.push_back(temp);
		} 
		sort(roads.begin(), roads.end(), cmp);
		//初始化并查集
		for(int i = 1; i <= n; i++) father[i] = i;
		int sumw = 0;
		for(int i = 0; i < m; i++){
			int a = find_father(roads[i].u);
			int b = find_father(roads[i].v);
			if(a != b){
				sumw += roads[i].w;
				father[a] = b;
			}
		} 
		cout<<sumw<<endl; 
	} 
	return 0;
} 

2022年4月22日

弄了三天决策树实验
2022年4月22日07:21:22

1183 2022年4月22日08:14:27

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

/*

输入样例#:

3
1.0 1.0
2.0 2.0
2.0 4.0

输出样例#:

3.41


*/
typedef struct node{
	double x;
	double y;
	int no;//编号 
}node;
typedef struct edge{
	int no1;
	int no2;
	double dis;
}edge;
const int maxn = 105;
int father[maxn];
int find_father(int x){
	if(father[x] == x) return x;
	return father[x] = find_father(father[x]);
}
bool cmp(edge a, edge b){
	return a.dis < b.dis;
}
int main(){
	int n;
	while(cin>>n){
		vector<node> nodes;
		for(int i = 0; i < n; i++){
			node temp;
			cin>>temp.x>>temp.y;
			temp.no = i;
			nodes.push_back(temp);
		}
		//int num = n*(n-1)/2;//一共可以有这么多条边
		vector<edge> edges;
		for(int i = 0; i < n; i++){
			for(int j = i+1; j < n; j++){
				edge temp;
				temp.no1 = i;
				temp.no2 = j;
				double s = (nodes[i].x - nodes[j].x)*(nodes[i].x - nodes[j].x) + (nodes[i].y-nodes[j].y)*(nodes[i].y-nodes[j].y);
				temp.dis = sqrt(s);
				edges.push_back(temp);
			}
		} 
		sort(edges.begin(), edges.end(), cmp);
		//初始化并查集
		for(int i = 0; i < n; i++){
			father[i] = i;
		} 
		//开始并查
		double sumdis = 0;
		for(int i = 0; i < edges.size(); i++){
			int a = find_father(edges[i].no1);
			int b = find_father(edges[i].no2);
			if(a != b){
				sumdis += edges[i].dis;
				father[a] = b; 
			}
		} 
		printf("%.2lf\n", sumdis);
	} 
	return 0;
} 

1234

  • 输入处理
  • cin读取char值时,与读取其他基本类型一样,将忽略空格和换行符。
  • scanf(“%c”, &c)和getchar© 都可以接收空格和换行
/*
The Council of Elders must choose to stop maintaining some roads.

输入样例#:

9
A 2 B 12 I 25
B 3 C 10 H 40 I 8
C 2 D 18 G 55
D 1 E 44
E 2 F 60 G 38
F 0
G 1 H 35
H 1 I 35
3
A 2 B 10 C 40
B 1 C 20
0

输出样例#:
复制

216
30


*/
typedef struct node {
	int a, b;
	int cost;
} node;
bool cmp(node a, node b) {
	return a.cost < b.cost;
}
const int maxn = 105;
char father[maxn];
int find_father(int x) {
	if(father[x] == x) return x;
	return father[x] = find_father(father[x]);
}
#include<bits/stdc++.h>
using namespace std;
int main() {
	int n;//村庄数
	while(cin>>n) {
		if(n == 0) break;
		vector<node> nodes;
		for(int i = 0; i < n-1; i++) {
			char a, b;
			int k;
			cin>>a;
			cin>>k;
			for(int j = 0; j < k; j++){
				node temp;
				cin>>b;
				cin>>temp.cost;
				
				temp.a = a - 'A';
				temp.b = b - 'A';
				nodes.push_back(temp);
			}
		}
		//
		sort(nodes.begin(), nodes.end(), cmp);
		for(int i = 0;i < n; i++){
			father[i] = i;
		} 
		//并查
		int sumcost = 0;
		for(int i = 0; i < nodes.size(); i++){
			int a = find_father(nodes[i].a);
			int b = find_father(nodes[i].b);
			if(a!= b){
				father[a] = b;
				sumcost += nodes[i].cost;
			}
		} 
		cout<<sumcost<<endl;
	}
	return 0;
}

最短路径问题

SPFA算法通用模板

  • 复杂度O(nm)
  • 核心思想就是BFS
  • 可以处理负边权
  • 可以处理负环
  • 可以输出最短路径
  • 注意,多组输入,初始化edges
  • 注意,结点是以1开始的,如果是0开始最后的是n-1
  • 循环n-1次
#include<bits/stdc++.h>
using namespace std;
/*

输入样例#:

2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0

输出样例#:
复制

3
2

*/
#define INF 0x3f3f3f3f
const int maxn = 101;
//邻接表的形式

typedef struct edge {
	int u, v, w;
	edge(int u, int v, int w):u(u), v(v), w(w) {}
} edge;
vector<edge> edges[maxn];
int vis[maxn];
int dis[maxn];
int num[maxn];//记录每个路口入队列(访问)的次数,大于n的时候说明存在负环
int p[maxn];
int n, m;//路口数目n,路的数目m
void init() {
	memset(dis, INF, sizeof(dis));
	memset(vis, 0, sizeof(vis));
	memset(num, 0, sizeof(num));
	for(int i = 0; i < maxn; i++) {
		edges[i].clear();
	}
}
bool spfa(int s) {

	//初始结点入队列
	queue<int> q;
	q.push(s);//起点放入队列
	num[s]++;
	vis[s] = 1;
	dis[s] = 0;//初始化距离
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = 0; i < edges[u].size(); i++) {
			int v = edges[u][i].v;
			int w = edges[u][i].w;
			if(dis[v] > dis[u]+w) {
				dis[v] = dis[u]+w;
				p[v] = u;//记录路径 
				if(!vis[v]) {
					q.push(v);
					vis[v] = 1;
					num[v]++;
					if(num[v] > n) return false;//有负环
				}
			}
		}
	}
	return true;
}

int main() {
	while(cin>>n>>m) {
		if(n == 0 && m == 0) break;
		init();
		for(int i = 0; i < m; i++) {
			int a, b, c;
			cin>>a>>b>>c;
			edges[a].push_back(edge(a, b, c));
			edges[b].push_back(edge(b, a, c));
		}
		spfa(1);
		cout<<dis[n]<<endl;
		int i = n;
		cout<<i; 
		while(p[i] != 1){
			cout<<"<---"<<p[i];
			i = p[i];
		} 
		cout<<"<---"<<1;
	}
	return 0;
}

dijkstra算法

  • 核心思想BFS,贪心,DP
  • 复杂度O(nlogn)
  • 没有负边权的时候性能好
  • 算法思想,先把所有边全部抹掉,
  • 初始结点的dis为0,其他结点的dis为INF
  • 将初始结点以及dis[s]组成的结构体node入队列,
  • 弹出未标记访问的最小的dis的结点u
  • 更新u的所有紧邻结点,如果dis被更新,将这些结点v以及dis[v]组成的Node加入到队列中。同时记录路径
  • 循环n次
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef struct edge{
	int u, v, w;
	edge(int u, int v, int w):u(u), v(v), w(w){}
}edge;
const int maxn = 101;
vector<edge> edges[maxn];//邻接表的形式 
int vis[maxn];
int dis[maxn];
int path[maxn];
int n, m;//n个结点,m条边 

//u的距离d 
typedef struct node{
	int d, u;
	node(int d, int u):d(d), u(u){}
	friend bool operator < (node a, node b){
		return a.d > b.d;
	}
}node;

void init(){
	memset(vis, 0, sizeof(vis));
	memset(dis, INF, sizeof(dis)); 
	for(int i = 0; i < maxn; i++){
		edges[i].clear();
	}
}
void dijkstra(int s){
	priority_queue<node> q;//使用优先队列(堆优化) 
	q.push(node(0, s));
	dis[s] = 0;
	while(!q.empty()) {//dijkstra本来是要循环n次,即初始结点也需要一次循环,所以上面初始结点入队列后vis不置1 
		node now = q.top();
		q.pop();
		int u = now.u;
		if(vis[u]) continue;
		vis[u] = 1;
		for(int i = 0; i < edges[u].size(); i++){
			int v = edges[u][i].v;
			int w = edges[u][i].w;
			if(dis[v] > dis[u]+w){
				dis[v] = dis[u]+w;
				path[v] = u;
				q.push(node(dis[v], v));//只有更新过的才加入队列 
			}
		} 
	}
}
int main(){
	while(cin>>n>>m){
		if(n == 0 && m == 0) break;
		init();
		for(int i = 0; i < m; i++){
			int a, b, c;
			cin>>a>>b>>c;
			edges[a].push_back(edge(a, b, c));
			edges[b].push_back(edge(b, a, c));
		}
		dijkstra(1);
		cout<<dis[n]<<endl; 
	}
	return 0;
}

Dijkstra和spfa的区别

见文章
D是贪心的思想,所以不能处理负边权
SPFA不需要去找离原点最近的点的,所以Dijkstra算法用的是小根堆优化,SPFA直接用的队列

floyd算法

  • 核心思想动态规划
  • 复杂度O(n^3)
  • 可以解决多源最短路径
#include<bits/stdc++.h>
using namespace std;
//其实是动态规划的思想
#define INF 0x3f3f3f3f
const int maxn = 101;
int mpt[maxn][maxn];
int n, m;


void floyd() {
	for(int k = 1; k <= n; k++) {
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= n; j++) {
				mpt[i][j] = min(mpt[i][j], mpt[i][k]+mpt[k][j]);
			}
		}
	}
}

int main() {
	while(cin>>n>>m) {
		if(n+m == 0) break;
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= n; j++) {
				if(i == j) mpt[i][j] = 0;//初始化,自己到自己是0,
				else mpt[i][j] = INF;//其他都是INF
			}
		}

		for(int i = 0; i < m; i++) {
			int a, b, c;
			cin>>a>>b>>c;
			if(c < mpt[a][b]) {//一定注意重复的边,选择边权最小的
				mpt[a][b] = c;
				mpt[b][a] = c;
			}
		}
		floyd();
		cout<<mpt[1][n]<<endl;
	}
	return 0;
}

拓扑排序

  • 不断寻找入度为0的点加入优先队列中
#include<bits/stdc++.h>
using namespace std;
const int maxn = 505;
bool mpt[maxn][maxn];//为1代表mpt[a][b]  a-->b 
int lev[maxn];//入度 
vector<int> v[maxn];//v[a]  保存a指向的所有点 
priority_queue<int, vector<int>, greater<int>> q;

void topo(int n){
	for(int i = 1; i <= n; i++){
		if(!lev[i]){
			q.push(i);//队列里面存放的是入度为0的点 
		}
	}
	int flag = 0;//出队的点的个数
	while(!q.empty()){
		int now = q.top();
		q.pop();
		if(flag) printf(" %d", now);
		else printf("%d", now);
		flag++;
		//以now为尾的头的入度减一
		for(int i = 0; i < v[now].size(); i++){
			int next = v[now][i];
			lev[next]--;
			if(!lev[next]) q.push(next);
		} 
	} 
	if(flag != n){
		printf("有环没有拓扑排序"); 
	}
}

int main(){
	int n, m;//n个队伍。m场比赛 
	while(cin>>n>>m){
		memset(mpt, 0, sizeof(mpt));
		for(int i = 0 ; i < m; i++){
			int a, b;
			cin>>a>>b;
			mpt[a][b] = 1;
		}
		
		//初始化所有点的入度lev,和指向的点v[i]
		for(int i = 1; i <= n; i++){
			v[i].clear();
			for(int j = 1; j <= n; j++){
				if(mpt[i][j]){
					lev[j]++;
					v[i].push_back(j);
				}
			}
		}
		
		topo(n);
		cout<<endl;
	}
	return 0;
} 

满200了
在这里插入图片描述

2022年4月23日

2022年4月23日08:24:11

复习最短路径的三个算法

  • dikstra,别忘记node结构体需要重写operator<运算符
  • 还有首次加入node(0, s)之后vis[s]不更新
  • spfa不要忘记初始化,还有在更新距离之后要更新path.但是加入队列需要先判断是否已经访问过
  • floyed使用动态规划就行,只用一个mpt数组

复习拓扑排序

  • 需要一个mpt[maxn][maxn]记录a—>b
  • 记录每个点的入度lev[maxn]
  • 记录每个点出去的结点vector int v[maxn]
  • 优先队列记录入度为0的点并按照小编号弹出

动态规划

可算到算法了

上楼梯1413

  • 注意需要使用long long 类型的dp[]
#include<bits/stdc++.h>
using namespace std;
const int maxn = 90;//楼梯的最大阶数1<=n<90 
typedef long long LL;
LL dp[maxn];
int main(){
	int n;
	while(cin>>n){
		//初始化dp
		dp[1] = 1;
		dp[2] = 2;
		for(int i = 3; i<= n; i++){
			dp[i] = dp[i-1]+dp[i-2];
		} 
		cout<<dp[n]<<endl;
	}
	return 0;
}

数塔问题

  • 使用递推的方式,从下至上利用递推公式求解
  • 可以直接确定的dp[][]是边界,用来初始化,最下一层就是边界
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1001;
int f[maxn][maxn];
int dp[maxn][maxn];
 
int main(){
	int n;
	while(cin>>n){
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= i; j++){
				cin>>f[i][j];
			}
		}
		//初始化dp
		for(int i = 1; i <= n; i++){
			dp[n][i] = f[n][i];
		} 
		//开始动态规划
		//从第n-1开始向上计算dp
		for(int i = n-1; i >= 1; i--){
			for(int j = 1; j <= i; j++){
				dp[i][j] = max(dp[i+1][j], dp[i+1][j+1]) + f[i][j];//不要忘记加上当前f[i][j]
			}
		} 
		//输出结果
		cout<<dp[1][1]<<endl; 
	}
	return 0;
} 

1197吃糖果,

  • 和斐波那契、走楼梯一样,
  • n<20的时候dp[n]没有超过int
#include<bits/stdc++.h>
using namespace std;
/*
(盒内共有 N 块巧克力,20 > N >0)。 
天可以吃一块或者两块巧克力。 
每天都吃巧克力,
问多少种不同的吃完巧克力的方案。
*/ 
const int maxn = 20;
int dp[maxn];
int main(){
	int n;
	while(cin>>n){
		//初始化dp
		dp[1] = 1;
		dp[2] = 2;
		for(int i = 3; i <= n; i++){
			dp[i] = dp[i-1]+dp[i-2];
		} 
		cout<<dp[n]<<endl;
	}
	return 0;
}

细菌的繁殖1033

这个代码绝了
将第i天细菌最中间一行的数目记作数列a[i]:a[1] = 1, a[2] = 5, a[3] = 13;
可以得到a[i] = 2i - 1
第i天细菌总数s[i] = 2
(a[1]+a[2]+…+a[i-1]) + a[i] = 2ii - 2*i + 1

#include<bits/stdc++.h>
using namespace std;
//const int maxn = 1001;//最大1000
//int dp[maxn]; 
int main(){
	int n;
	cin>>n;
	while(n--){
		int day;
		cin>>day;
		cout<<2*day*(day-1)+1<<endl;
	} 
	return 0;
}

最大子序和

  • 使用动态规划
  • dp[i]表示以nums[i]结尾的序列
  • 因为必须是dp[i]结尾,所以只有两种情况
    在这里插入图片描述
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1000001;//最多maxn个数
LL nums[maxn]; 
LL dp[maxn];
int main(){
	int n;
	while(cin>>n){
		//输入 
		for(int i = 0; i < n; i++){
			cin>>nums[i];
		}
		//动态规划
		//初始化边界 
		dp[0] = nums[0];//第一个为0 
		LL ans = nums[0]; 
		for(int i = 1; i <= n; i++){
			dp[i] = max(nums[i], nums[i]+dp[i-1]);
			ans = max(ans, dp[i]);
		} 
		cout<<ans<<endl;
	}
	return 0;
}

1642翻转字符串后1的个数最多

必须翻转一个0的数目多余1的数目的子串,而且是差值越大越好,那么我们将1看作-1,0看作1,求最大连续区间和,该值就是我们最多可以通过反转再得到的1的数目,加上串中本来的1的数目就是答案

  • 在一段序列中,0的贡献是1,1的贡献是-1,这样最大字段和就是0比1多的个数。
  • 跟最大子段和有点区别,这个可以一个也不选,上面那个不能不选
#include<bits/stdc++.h>
using namespace std;
const int maxn = 10000005;
int dp[maxn];
int a[maxn];
string s; 
int main(){
	int n;
	while(cin>>n){
		cin>>s;
		int num1 = 0;
		for(int i = 0; i < n; i++){
			if(s[i] == '0') a[i] = 1;
			else {
				a[i] = -1;
				num1++;
			}
		}
		//动态规划
		//初始化
		dp[0] = a[0];
		int maxsum = 0;//注意是0
		for(int i = 1; i < n; i++){
			dp[i] = max(a[i], dp[i-1]+a[i]);
			maxsum = max(maxsum, dp[i]);
		} 
		cout<<maxsum+num1<<endl;
	}
	return 0;
}

LIS的长度

dp[i]:表示以a[i]结尾的LIN的长度
dp[i] = max(1, dp[j]+1), j是i之前的
是序列是可以不连续的

#include<bits/stdc++.h>
using namespace std;
const int maxn = 101;
int a[maxn];
/*
8
1 2 3 -9 3 9 0 11

6
*/
int main(){
	int n;
	while(cin>>n){
		for(int i = 1; i <= n; i++){
			cin>>a[i];
		}
		//动态规划
		//初始化边界dp[i] = 1;
		vector<int> dp(n+1, 1);
		int ans = -1;
		for(int i = 1; i <= n; i++){
			for(int j = 1; j < i; j++){
				if(a[i] >= a[j]){
					dp[i] = max(dp[i], dp[j]+1);
				}
			}
			ans = max(ans, dp[i]);
		} 
		cout<<ans<<endl;
	}	
	return 0;
}

1257LIS的和

这个是单调递增

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1002;
int a[maxn];
int dp[maxn];
int main(){
	int n;
	while(cin>>n){
		for(int i = 1; i <= n; i++){
			cin>>a[i];
		}
		//初始化dp边界
		//动态规划
		int sum = 0;
		for(int i = 1; i <= n; i++){
			dp[i] = a[i];
			for(int j = 1; j < i; j++){
				if(a[i] > a[j]){//最长上升子序和,必须是单调递增,和单调不减不一样 
					dp[i] = max(dp[i], dp[j]+a[i]);
				}
			}
			sum = max(sum, dp[i]);
		} 
		cout<<sum<<endl;
	}
	return 0;
}

拦截导弹1256

  • 求的是单调不增即<=
	#include<bits/stdc++.h>
    using namespace std;
    const int maxn = 27;
    int dp[maxn],a[maxn],n;
    int main(){
    	while(cin>>n){
    		for(int i=1;i<=n;i++){
    			cin>>a[i];
    		}
    		int ans = 0;
    		for(int i = 1; i <= n; i++){
				dp[i] = 1;
				for(int j = 1; j < i; j++){
					if(a[i] <= a[j]){
						dp[i] = max(dp[j]+1, dp[i]);
					}
					ans = max(dp[i], ans);
				}
			}
    		cout<<ans<<endl;
    	}
    	return 0;
    }

1253合唱队排队

  • 求最长下降的时候是倒着
#include<bits/stdc++.h>
using namespace std;
const int maxn = 110;
int a[maxn];
int f[maxn];
int g[maxn]; 
int main(){
	int n;
	while(cin>>n){
		for(int i = 1; i <= n; i++){
			cin>>a[i];
		}
		//求最长递增
		for(int i = 1; i <= n; i++){
			f[i] = 1;
			for(int j = 1; j < i; j++){
				if(a[i] > a[j]){
					f[i] = max(f[i], f[j]+1);
				}
			}
		} 
		//求以i开始的最长递减,倒着
		for(int i = n; i ; i--){
			g[i] = 1;
			for(int j = n; j > i; j--){
				if(a[j] < a[i]){
					g[i] = max(g[i], g[j]+1);
				}
			}
		} 
		//遍历求
		int res = 0;//剩下的人
		for(int i = 1; i <= n; i++){
			res = max(f[i]+g[i]-1, res);
		} 
		cout <<n-res<<endl;
	}
	return 0;
}

2022年4月24日

2022年4月24日07:14:39

复习拓扑排序1566(30分钟)

在这里插入图片描述在这里插入图片描述

  • 一定注意编号是从1开始,初始化的时候要从1-n
  • 需要一个邻接表,一个入度数组,一个优先队列
  • 优先队列需要greater
#include<bits/stdc++.h>
using namespace std;

/*

输入描述:

输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示队伍的个数,M表示接着有M行的输入数据。
接下来的M行数据中,每行也有两个整数P1,P2表示即P1队赢了P2队。

输出描述:

给出一个符合要求的排名。输出时队伍号之间有空格,最后一名后面没有空格。

其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;
输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。

输入输出样例
输入样例#:

4 3
1 2
2 3
4 3

输出样例#:

1 2 4 3


*/
const int maxn = 505;//最多505个数
int lev[maxn];//记录每个结点的入度
vector<int> v[maxn];//邻接表 
priority_queue<int, vector<int>, greater<int>> q;//加入入度为0的结点 
bool topo(int n){
	//先找一个入度为0的点(起点)
	for(int i = 1; i <= n; i++){
		if(lev[i] == 0){
			q.push(i);
		}
	} 
	//开始了
	int num = 0;// 统计入队的元素个数 
	while(!q.empty()){
		int a = q.top();
		q.pop();
		if(num){
			printf(" %d", a);
		} else{
			printf("%d", a);
		}
		num++;
		for(int i = 0; i < v[a].size(); i++){
			int b = v[a][i];
			lev[b]--;
			if(lev[b] == 0){
				q.push(b);
			}
		}
	} 
	if(num == n) return true;
	else return false;
}
int main(){
	int n, m;
	while(cin>>n>>m){
		//循环初始化 
		memset(lev, 0, sizeof(lev));
		for(int i = 1; i <= n; i++){
			v[i].clear();
		}
		//输入初始化入度,邻接表 
		for(int i = 0; i < m; i++){
			int a, b;
			cin>>a>>b;
			v[a].push_back(b); 
			lev[b]++;
		}
		topo(n);
		cout<<endl; 
	}
	return 0;
}

复习最短路径

spfa20分钟—
2022年4月24日11:28:44,可算复习完了

LCS 1293

dp[i][j]
注意dp的下标1开始,s的下标0开始

#include<bits/stdc++.h>
using namespace std;
const int N = 101;
int dp[N][N];//dp[i][j]表示a的前i个和b的前j个的LCS长度 
int main(){
	string s1, s2;
	while(cin>>s1>>s2){
		memset(dp, 0, sizeof(dp));
		int len1 = s1.size();
		int len2 = s2.size();
		
		for(int i = 1; i <= len1; i++){
			for(int j = 1; j <= len2; j++){
				if(s1[i-1] == s2[j-1]){
					dp[i][j] = dp[i-1][j-1]+1;
				}else{
					dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
				}
			}
		} 
		cout<<dp[len1][len2]<<endl;
	}
	return 0; 
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值