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;
}