前言与提示
枚举和模拟 是两大类简单基本算法题!机试的简单题就看这!
另外刚学到简单题,经常会在多组输入问题上出错:
在此引用一篇N诺的文章
C循环多组输入:while(scanf("%d%d",&a,&b)!=EOF){}
scanf()是有返回值的,返回值为成功赋值的变量个数!
C++循环多组输入:while(cin>>a>>b){}
2.1 枚举
基本思路
算法思路:利用计算机运算速度快的特点,对问题的所有可能答案一一列举,并逐一检验,符合条件的保留,不符合的丢弃。
枚举法问题 首先要考虑运算次数
例题1:枚举量=10x10x10=1000 √可接受
例题2:枚举量=4x10000=40000 或 10x10x10x10=10000 √可接受
例题3:(256)^2=65536 5位 求反序数计算5次
枚举量=5x(256+1)=1285 √可接受
例题1 abc(清华复试题)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/912b15e237ef44148e44018d7b8750b6
#include <iostream>
#include <cstdio>
using namespace std;
int main(){
for(int a = 0 ; a<=9 ;a++){
for(int b = 0 ; b<=9 ;b++){
for(int c = 0 ; c<=9 ;c++){
if(100*a + 110*b + 12*c == 532) printf("%d %d %d",a,b,c);
}
}
}
}
例题2 反序数(清华复试题)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/e0d06e79efa44785be5b2ec6e66ba898
在次提供解法两种
一种为书上提供
求反序数的Reverse函数 在进制转换中有类似的操作
另一种是自己没看书的解法 思路与例题1类似
#include <iostream>
#include <cstdio>
using namespace std;
//法1:书上解法
int Reverse(int N){//N=321
int revN = 0;//求反序数123
while(N!=0){
revN *= 10;
revN += N % 10; //求出末位给到revN 即 321求出1
N /= 10; //N删去末位 即321变32
}
return revN;
}
int main(){
for(int i = 1000 ; i<=9999 ;i++){
if(i*9 == Reverse(i)) printf("%d",i);
}
}
//法2:自写穷举法
int main(){
for(int a = 1 ; a<=9 ;a++){//需要注意a不可等于0 否则会出错
for(int b = 0 ; b<=9 ;b++){
for(int c = 0 ; c<=9 ;c++){
for(int d = 0 ; d<=9 ;d++){
if(9*(1000*a+100*b+10*c+d) == a+10*b+100*c+1000*d){
int N = 1000*a+100*b+10*c+d;
printf("%d",N);
}
}
}
}
}
}
例题3 对称平方数1(清华复试题)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/a84d46d5321f4e20931cb725e6c74fad
#include <iostream>
#include <cstdio>
using namespace std;
//求反序数
int Reverse(int N){
int revN = 0;
while(N!=0){
revN *= 10;
revN += N % 10;
N /= 10;
}
return revN;
}
int main(){
for(int i = 0 ; i<=256 ; i++){
if(i*i == Reverse(i*i)) printf("%d\n",i);
}
}
习题2.1 与7无关的数(北大复试题)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/776d401bf86d446fa783f0bef7d3c096
题目限制了输入的数字n<100,故采用下方代码解法Judge即可简单解出
#include <iostream>
#include <cstdio>
using namespace std;
bool Judge(int n){
if(n % 7 == 0)//被7整除
return false;
if(n % 10 == 7)//个位为7
return false;
if(n / 10 == 7)//十位为7
return false;
return true;
}
int main(){
int sum = 0;
int n = 0;
while(cin >> n){
for(int i = 1 ; i<= n ;i++){
if(Judge(i))
sum += i*i;
}
printf("%d",sum);
}
}
若不考虑n<100的限制,普适的Judge函数应为
bool Judge(int n){
int j = n;
if(n%7==0) //判断是否被7整除
return false;
else{
while(j!=0){
j=j%10;//判断末位是否==7
if(j==7){
return false;
break;
}
j=j/10;//逐个删去末位 求新的末位
}
}
return true;
}
习题2.2 百鸡问题(哈工大复试题)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/01d161052db64c249a47fc723b4fd5db
#include <iostream>
#include <cstdio>
using namespace std;
int main(){
int n = 0;
while(cin >> n){
for(int x = 0 ; x<=100 ;x++){
for(int y = 0 ; y<=100-x ;y++){
for(int z = 0 ; z<=100-x-y ;z++){
if(15*x + 9*y + z <= 3*n && x+y+z==100)
printf("x=%d,y=%d,z=%d\n",x,y,z);
}
}
}
}
}
习题2.3 Old Bill(上交复试题)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/17a30153e092493e8b4d13f321343927
#include <iostream>
#include <cstdio>
using namespace std;
int main(){
int n = 0;
int x,y,z;
int first,last,tf,tl;
while(scanf("%d", &n)!=EOF){
cin>>x>>y>>z;
int base = 1000*x + 100*y + 10*z;
int price = 0;
for(first = 1 ; first<=9 ;first++){
for(last = 0 ; last<=9 ;last++){
if((10000*first + base + last)%n==0){
tf = first;
tl = last;
if((10000*first + base + last)/n>price){
price = (10000*first + base + last)/n;
tf = first;
tl = last;
}
}
}
}
if(price==0) printf("%d\n",price);
else printf("%d %d %d\n",tf,tl,price);
//tf,tl存初次的有解值,最后只输出单价最大对应的
}
}
2.2 模拟
基本思路
没什么算法 一般就是按照输出的规则格式 正确输出即可
主要包括三类:
(1)图形排版
(2)日期问题
(3)其他
图形排版
例题4 输出梯形(清华复试)
#include <iostream>
using namespace std;
//分行列 找规律
int main(){
int h;
while(cin >> h){
int row = h;
int col = h+2*(h-1);
for(int i =0;i<row;i++){
for(int j =0;j<col;j++){
if(j<col-h-2*i) cout <<" ";
else cout <<"*";
}
cout<<"\n";
}
}
return 0;
}
例题5 叠筐
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/2d6505bf0d38479c9bff66e10fe39a5c
下面的代码为个人解法,已AC。
#include <iostream>
using namespace std;
int main(){
int n;
char a,b;
int flag=1;
while(cin >> n >>a >>b){
//从第二个图像开始,每个输出之间需要打印空行 题目格式陷阱
if(flag==1) flag=0;
else printf("\n");
if(n==1) cout<<a<<endl;
else{
int t = n/2;
for(int i=0; i<n ;i++){
for(int j =0;j<n;j++){
if((i==0&&j==0)||(i==n-1&&j==n-1)||(i==n-1&&j==0)||(i==0&&j==n-1)) cout <<" ";
else{
//这几句是重点
int k=abs(i-n/2);
int t=abs(j-n/2);
if((k>t?k:t)%2==0) cout <<a;
else cout <<b;
}
}
cout<<"\n";
}
}
}
return 0;
}
书上提供的代码用数组存储,先通过求当前圈边长和字符,再为上下左右边赋值,最终打印数组得出,部分代码如下:
int length = n-2*i;
char c;
if((n/2 - i)%2==0) c=a;
else c=b;
for(int k = 0;k<length;k++){
matrix[i][i+k] = c; //上边
matrix[i+k][i] = c; //下边
matrix[j][j-k] = c; //右边
matrix[j-k][j] = c; //左边
}
习题2.4 Repeater(北大复试)
poj 3768
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/97fd3a67eff4455ea3f4d179d6467de9
本题思路:用一个二维数组template储存模板,这个模板就是每次repeat使用的。用temp储存repeat的中间过程,用picture储存repeat的结果。这里有一点需要注意,repeat的数目是输入的Q-1。
repeat过程:用分形把原图形扩大。遍历模板,在模板是空格处,在picture相应的位置放一个与上一步repeat结果大小一样的空白区;在模板不是空格处(有图形),在picture相应的位置放上一步repeat的结果图形。
若模板size为3x3,则repeat一次后(Q=2),最终图形的大小变成原来大小的3x3倍。
上述思路讲解者:
https://blog.csdn.net/ujingzanghai/article/details/79163851
这位写的思路非常清晰 不过代码略微比dfs复杂一些
此外,我认为:分形后图形的宽度求解pow(n,Q)即可,比第三个引用更好理解。比如3x3的要Q=2,则新的宽为3^2.
本题用到了分形、DFS、递归模拟的思想
在此引用几篇其他大佬的文章:
①分形(Fractal)介绍:https://blog.csdn.net/weixin_44740740/article/details/104545017
如果B(n-1)表示n-1度的盒分形,则n度的盒分形递归定义如下:
B(n - 1) B(n - 1)
B(n - 1)
B(n - 1) B(n - 1)
②使用DFS解法:
参考下篇的代码,本人对DFS的理解还不到位,本题仅是读懂了代码含义 :https://blog.csdn.net/RPG_Zero/article/details/100568116
DFS可用于全排列,排版。
在此引用一篇DFS的很好的博文,供以后学习:
https://blog.csdn.net/weixin_43272781/article/details/82959089
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
char picture[3005][3005];
char str[5][5];
int n;
void dfs(int Q, int x, int y){
int i, j, size;
if (Q == 1){//Q为1 则
for (i = 0; i<n; i++){
for (j = 0; j<n; j++){
//按照模板复制一遍就结束
picture[x + i][y + j] = str[i][j];
}
}
return;
}
size = pow(n, Q - 1);
for (i = 0; i<n; i++){
for (j = 0; j<n; j++){
//如果对应位置不是空格 就在相应的位置放上一步的结果图形
//dfs去找 若Q-1=1则直接打印模板 否则再递归
if (str[i][j] != ' ') dfs(Q - 1, x + i*size, y + j*size); //i*size 表示空出i*size个空格位置 空格处,放一个与上一步结果大小一样的空白区
}
}
}
int main(void){
int i, j, Q, size;
//当输入一个整型,且这个整型的数不是0时成立
while (scanf("%d", &n), n){
getchar();//读掉换行符
for (i = 0; i<n; i++){
gets(str[i]);//读入n行原template
}
scanf("%d", &Q);//读入Q
size = pow(n, Q);//求宽
//初始化为空格
//memset(picture, ' ', sizeof(picture));
for (i = 0; i<size; i++){
for (j = 0; j<size; j++){
picture[i][j] = ' ';
}
picture[i][size] = '\0'; //设置每行字符串的结束符
}
dfs(Q, 0, 0); //从(0,0)位置处递归
for (i = 0; i<size; i++){
puts(picture[i]); //每次输出一行
}
}
return 0;
}
习题2.5 Hello World for U(浙大复试)
poj 3768
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/87df6ec7ef344dd6bbaeeaca84f3a994
#include <iostream>
#include <cstring>
using namespace std;
int main(){
char U[100];//字符串<80
int i = 0, j = 0, k = 0;
cin >> U;
int len = strlen(U);
int aver = (len + 2) / 3; //为了成为矩形/正方形,平均=竖的边
int ju = len - aver * 2; //最下面的边除两端有多长
aver--;
//按行输出除底边外的上两边
for (i = 0; i < aver; i++){
cout << U[i];
for (int k = 0; k < ju; k++){
cout <<" ";
}
cout << U[len - 1 - i] << endl;
}
j = len - 1 - i;//i为竖边都是多少个字符
//输出最后一行
for (i; i <= j; i++){
cout << U[i];//最后一行横着打印去头去尾的一段
}
return 0;
}
日期问题
日期类运算频繁入选考题,但有规律可循。
核心规律:
①预处理:“空间换时间”,二维数组提前保存平年和闰年的每月天数,不用逐月判断。真正处理时仅需O(1)的时间复杂度就能读出数据。
②注意闰年!
闰年判断规则:当年数不能被100整除时,若能被4或400整除,则为闰年。
(year %4 == 0 && year%100!=0)||(year%400 ==0)
“闰年之间不一定相邻4年!1896与1904”
例题6 今年的第几天(清华复试题)
题目OJ网址(牛客网):
https://www.nowcoder.com/practice/ae7e58fe24b14d1386e13e7d70eaf04d?tpId=60&tqId=29491&tPage=0&ru=/kaoyan/retest/1001&qru=/ta/tsing-kaoyan/question-ranking
#include <iostream>
using namespace std;
//预处理
int monthday[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}};
int main(){
int year,month,day;
while(cin>>year >>month >>day ){
int sum = 0,rec=-1;
//判断闰年
if((year%4==0 && year%100!=0)||(year%400 ==0)) rec=1;
else rec=0;
for(int i = 0;i<month;i++){
sum +=monthday[rec][i];
}
sum +=day;
cout<<sum<<endl;
}
return 0;
}
例题7 打印日期(华科复试题)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/b1f7a77416194fd3abd63737cdfcf82b
注:本题涉及打印月和日时,自动补零的操作。
对相关printf输出格式进行说明如下:
//补0与输出场宽
int a = 4;
printf("%3d",a);//输出 4,超过3位按实际输出,3代表位宽
printf("%03d",a);//输出004,超过3位按实际输出,0前面补0占位,3表位宽
double b = 123.456,c = 12.34;
printf("%5.2f",b);//输出123.46,输出场宽5,小数点后2位,超过则四舍五入
printf("%5.2f",c);
//输出12.34,小数点后2位,不足5位但实践结果?左对齐?
//默认打印格式为左对齐
printf("%-10d\n",101010);//加上“-”也可实现左对齐
//实现右对齐
printf("%10d",101010);//在%和d之间加上数字宽度
本题AC代码
#include <iostream>
using namespace std;
int monthday[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}};
int main(){
int year,sum;
while(cin>>year>>sum){
int month=0,day=0;
int rec=-1;
if((year%4==0 && year%100!=0)||(year%400 ==0)) rec=1;
else rec=0;
int i;
for(i = 0;sum>0;i++){
if(sum>monthday[rec][i]) sum -=monthday[rec][i];
else break;
}
month = i;
day = sum;
printf("%04d-%02d-%02d\n",year,month,day);
}
return 0;
}
例题8 日期累加(北理复试题)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/eebb2983b7bf40408a1360efb33f9e5d
本题思路:例题6+7的结合,即正向(知道日期求天数)和反向(知道天数求日期)的结合!
我自己的解题思路太复杂,尤其在累加>365/366的时候处理的不好!
下面代码的解法:先知道初始日期求累加之后的总天数,再通过总天数求最终日期。
#include <iostream>
using namespace std;
//预处理
int monthday[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 IsLeapYear(int year){
return (year%4==0 && year%100!=0)||(year%400==0);
}
//平年闰年天数
int NumberOfYear(int year){
if(IsLeapYear(year)){
return 366;
}
else{
return 365;
}
}
int main(){
int num = 0;
cin>>num;
while(num--){
int y,m,d,sum,rec=-1;
int month = 0,day = 0;
cin>>y>>m>>d>>sum;
rec = IsLeapYear(y);
for(int j = 0;j<m;j++){
sum += monthday[rec][j];
}
sum += d;
//上述代码求出加上若干天的总天数
while(sum>NumberOfYear(y)){
sum -= NumberOfYear(y);
y++;
}
rec = IsLeapYear(y);//求出最终年份闰/平
while(sum>monthday[rec][month]){
sum -= monthday[rec][month];
month++;
}
day = sum;
printf("%04d-%02d-%02d\n",y,month,day);
}
return 0;
}
Mark:明确告知n组的多组输入
while(caseNumber--){}
习题2.6 日期差值(上交复试)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/ccb7383c76fc48d2bbc27a2a6319631c
#include <iostream>
#include <cmath>
using namespace std;
//预处理
typedef struct date{
int y, m, d;
}Date;
int monthday[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 IsLeapYear(int year){
return (year%4==0 && year%100!=0)||(year%400==0);
}
//求总天数,不过这个做法稍微感觉计算量有点大,如果先比较year可能会更好
int sumdays(Date x){
int sum=0, i;
int y=x.y, m=x.m, d=x.d;
for(i=0; i<y; i++){
if(IsLeapYear(i)) sum+=366;
else sum+=365;
}
for(i=1; i<m; i++){
sum += monthday[IsLeapYear(y)][i];
}
sum += d;
return sum;
}
int main(){
Date a,b;
while(scanf("%4d%2d%2d", &a.y, &a.m, &a.d)!=EOF){
scanf("%4d%2d%2d", &b.y, &b.m, &b.d);
printf("%d\n", abs(sumdays(a)-sumdays(b))+1);
}
return 0;
}
习题2.7 Day of Week(上交复试)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/a3417270d1c0421587a60b93cdacbca0
#include <iostream>
#include <cmath>
#include <string.h>
using namespace std;
//预处理
typedef struct date{
int y,m,d;
string month;
}Date;
int monthday[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}};
string monthName[13] = {
"","January","February","March","April","May","June","July",
"August","September","October","November","December"
};
string weekName[7] = {
"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
};
//判断平年还是闰年
bool IsLeapYear(int year){
return (year%4==0 && year%100!=0)||(year%400==0);
}
//求总天数,不过这个做法稍微感觉计算量有点大,如果先比较year可能会更好
int sumdays(Date x){
int sum=0, i;
int y=x.y, m=x.m, d=x.d;
for(i=0; i<y; i++){
if(IsLeapYear(i)) sum+=366;
else sum+=365;
}
for(i=1; i<m; i++){
sum += monthday[IsLeapYear(y)][i];
}
sum += d;
return sum;
}
int main(){
Date a,current;
current.d=12;
current.m=7;
current.y=2020;
while(cin>>a.d>>a.month>>a.y){
for (a.m = 1; a.m<13; a.m++) {
int jud = (a.month).compare(monthName[a.m]);
if(!jud) break;
}
int i = sumdays(a) - sumdays(current);
//这里要分类讨论,因为输入日期若比我们已知日期早
//则天数减去,星期也应该倒着算~
if(i>=0) cout << weekName[i % 7]<<endl;
else cout << weekName[7-((-i) % 7)]<<endl;
}
return 0;
}
习题2.8 日期类(北理复试)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/130aa2d7d1f5436b920229dca253893b
简单题,别去考虑算总天数什么的,那就太复杂了。
#include <iostream>
using namespace std;
int monthday[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 IsLeapYear(int year){
return (year%4==0 && year%100!=0)||(year%400==0);
}
int main(){
int num = 0;
cin>>num;
while(num--){
int y,m,d,sum,rec=-1;
cin>>y>>m>>d;
rec = IsLeapYear(y);
if(++d > monthday[rec][m]){
m++;
d = 1;
if(m>12){
y++;
m=1;
}
}
printf("%04d-%02d-%02d\n",y,m,d);
}
return 0;
}
其他模拟
例题9 剩下的树(清华复试题)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/f5787c69f5cf41499ba4706bc93700a2
直接用一个数组表示所有的树,有树置为1,无树置为0即可。
若交叉区间对区间进行更新,需要多分类讨论,且容易重复或者漏掉
#include <iostream>
using namespace std;
const int Max = 10001;
int road[Max];
int main(){
int L,M;
cin>>L>>M;
for(int i=0; i<=L; i++){
road[i] = 1;
}
int sum = L + 1;
while(M--){
int left,right;
cin>>left>>right;
for(int j=left; j<=right; j++){
if(road[j]==1){
road[j] = 0;
sum--;
}
}
}
cout<<sum<<endl;
return 0;
}
例题10 手机键盘(清华复试题)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/20082c12f1ec43b29cd27c805cd476cd
按键上有的3个字母 有的4个字母!
日期模拟的预处理策略用起来!
#include <iostream>
using namespace std;
//预处理
int keyboard[26]={1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,4,1,2,3,1,2,3,4};
int main(){
string input;
while(cin>>input){
int sum = 0;
for(int i = 0;i< input.size();i++){
sum += keyboard[input[i]-'a'];//按键
//判断两个字母在同一个按键上
if(i!=0 && input[i]-input[i-1] == keyboard[input[i]-'a'] - keyboard[input[i-1]-'a'])
sum += 2;//等待
}
printf("%d\n",sum);
}
return 0;
}
例题11 XXX定律(浙大复试题)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/20082c12f1ec43b29cd27c805cd476cd
输入0时结束的写法:
while(cin>>num){
if(num==0) break;//输入0时结束
}
#include <iostream>
using namespace std;
int main(){
int num;
while(cin>>num){
if(num==0) break;//输入0时结束
int sum = 0;
while(num!=1){
if(num%2==0){
num = num/2;//偶数
sum++;
continue;
}
else{
num = (3*num+1)/2;
sum++;
continue;
}
}
cout<<sum<<endl;
}
return 0;
}
习题2.9 Grading(浙大复试题)
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/23e3244406724ffa8330760f640c8149
基本无难度,按部就班即可实现!
关于三个数求最大值的代码:
max = a > b ? a : b;
if(max < c) max = c;
关于从大到小排序的代码:
for(j=0;j<N-1;j++){
for(x=0;x<N-j-1;x++){
if(b[x]<b[x+1]){
temp=b[x];
b[x]=b[x+1];
b[x+1]=temp;
}
}
}
for(j=0;j<N;j++)
printf("%d\n", b[j]);
#include <iostream>
#include <cmath>
using namespace std;
int main(){
int P,T,G1,G2,G3,GJ;
double res;
while(cin>>P>>T>>G1>>G2>>G3>>GJ){
if(abs(G1-G2)<=T){
res = (G1+G2)/2.0;
}
else if((abs(G3-G1)<=T)||(abs(G3-G2)<=T)){
if(!((abs(G3-G1)<=T)&&(abs(G3-G2)<=T))){
if(abs(G3-G1)<abs(G3-G2)) res = (G1+G3)/2.0;
else res = (G2+G3)/2.0;
}
}
else if((abs(G3-G1)<=T)&&(abs(G3-G2)<=T)){
//求最大值的代码
int maximum = G1;
if(maximum<G2) maximum = G2;
if(maximum<G3) maximum = G3;
res = maximum;
}
else if(!((abs(G3-G1)<=T)||(abs(G3-G2)<=T))){
res = GJ;
}
printf("%.1f",res);//case 1
}
return 0;
}
习题2.10 路径打印(上交复试题)※
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/64b472c9bed247b586859978d13145ad
思路:多叉树+DFS
这题 其实还不是特别明白!下文代码引用自这篇博文:https://blog.csdn.net/qq_31113079/article/details/74514894
#include <iostream>
#include <cstring>
using namespace std;
typedef struct Node{
char s[60];
Node *son;
Node *next_sibling;
}Node;
Node node[30];
void DFS(Node *root, int depth){
Node *p = root->son;
while(p){
for(int i = 1; i <= depth; i++){
cout << " ";
}
int len = strlen(p->s);
cout << p->s << endl;
DFS(p, depth+1+len);//这里输出格式一定要注意!!
p = p->next_sibling;
}
}
int main(){
int n;
while(scanf("%d", &n), n){
Node *rt = new Node();//根结点
rt->son = NULL;
rt->next_sibling = NULL;
while(n--){//构造一棵多叉树
char s[60];
scanf("%s", s);
int len = strlen(s);
char tmp[60];
memset(tmp, 0, sizeof(tmp));
int flag = 0;
Node *p = rt;
for(int i = 0; i <= len-1; i++){
if(s[i] != '\\'){//把当前目录路径名存到tmp里 flag表示目录个数
tmp[flag++] = s[i];
}
if(s[i] == '\\' || i == len-1){
if(p->son == NULL){ //当前(根)节点没有儿子
Node *tt = new Node();//新的子目录
//将指定长度的字符串复制到字符数组中 把tmp中flag长度的字符串复制到tt->s
strncpy(tt->s, tmp, flag);
tt->son = NULL;
tt->next_sibling = NULL;
p->son = tt;//先把新子目录与之前的连接起来
p = tt;//p指向新目录方便下一次循环
}
else{ //根节点有儿子
Node *pre = p;
p = p->son;//p指向根节点的儿子
while(p && strcmp(p->s, tmp) < 0){//比较当前子目录与已有儿子的大小(从小到大排序)
pre = p;
p = p->next_sibling;//和根节点的所有兄弟比较一遍
}
if(p == NULL){//比较结束
Node *tt = new Node();
strncpy(tt->s, tmp, flag);
tt->son = NULL;
tt->next_sibling = NULL;
pre->next_sibling = tt;//由于当前子目录字符大,作为下一个兄弟
p = tt;
}
else if(strcmp(p->s, tmp) > 0){//已有儿子比当前子目录的字符大
Node *tt = new Node();
strncpy(tt->s, tmp, flag);
tt->son = NULL;
tt->next_sibling = p;//插在已有儿子p之前
//与上一根的连接son/nextsibilign也要更新
if(p == pre->son)
pre->son = tt;
else if(p == pre->next_sibling)
pre->next_sibling = tt;
p = tt;
}
//等于节点值的只需要找到这个节点
}
memset(tmp, 0, sizeof(tmp));//tmp清零
flag = 0;
}
//printf("i:%d s[i]:%c\n", i, s[i]);
}
}
DFS(rt, 0);//深度优先遍历多叉树
cout << endl;
}
return 0;
}
习题2.11 坠落的蚂蚁(北大复试题)※
题目OJ网址(牛客网):
https://www.nowcoder.com/questionTerminal/fdd6698014c340178a8b1f28ea5fadf8
这题上来没看懂,也是没啥思路,需要一定逻辑分析.思路指导参考了:
https://blog.csdn.net/travelalong/article/details/18134911
https://www.cnblogs.com/liangrx06/p/5083868.html
题目看起来很复杂,要模拟的话也比较麻烦,但有一个事实:
两只非A(A蚂蚁是刚开始静止的那只蚂蚁)蚂蚁相碰的时候交换各自的速度其实相当于两只蚂蚁还是各走各的路,没有碰头,速度不变。
在A左边并且初速度向左-1的蚂蚁&在A右边并且初速度向右+1的蚂蚁,不会对A造成任何影响。这两种类型的蚂蚁可以忽略掉。
只要A左边的蚂蚁数量,等于所有蚂蚁中往左走的数/A右边的等于向右走的,那么A就不会掉下去。
现在考虑所有蚂蚁的相对位置不变!
如果A左边的蚂蚁数量<向左走的蚂蚁数量,那么它总会加入向左走的大军最后掉落。
如果是第k个向左走的蚂蚁下去了,那么他之前的向左走的蚂蚁都下去了,反映到相对位置上来说,就是树枝上左边k-1只都下去了。
那么这一瞬间掉下去的想必就是相对位置在第k的蚂蚁了,也就是A。
即一开始所有向左走的蚂蚁中,第k个蚂蚁要走多远,就是最终答案!!
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct Ant{
int position;
int direct;//方向
bool operator < (const Ant &a) const{
return position<a.position;
}
};
int main(){
int n;
while(scanf("%d",&n) != EOF){
vector<Ant> ant(n);
for(int i = 0; i<n; i++)
scanf("%d %d",&ant[i].position,&ant[i].direct);
sort(ant.begin(),ant.end());
//接下来要做的就是找到静止的那只的位置,为此我们要先排序
//这样找到的静止的蚂蚁左边有几只就出来了
int target,toLeft = 0; //这里选用向左走的为基准来做
for(int i = 0; i<n; i++){ //遍历所有蚂蚁
if(ant[i].direct == 0) target = i;
if(ant[i].direct == -1) toLeft++;
}//现在的target就是静止的蚂蚁左边的数量了
bool flag = false;
int ans;
if(toLeft == target) flag = true;
else if (toLeft > target)//这样的话我们要找的就是所有向左走的蚂蚁中,第target蚂蚁
{
int cnt = 0;//计数器
for(int i = 0; i<n; i++){
if(ant[i].direct == -1 && cnt == target){
ans = ant[i].position;
break;
}
else if(ant[i].direct == -1) cnt++;
}
}
else{ //向左走的蚂蚁少,那么目标蚂蚁会向右落下
int cnt = 0;
for(int i = n - 1; i>=0; i--){
if(ant[i].direct == 1 && cnt == n - target - 1){//相应的变化,cnt要变成静止蚂蚁右边的蚂蚁数量
ans = 100 - ant[i].position;
break;
}
else if(ant[i].direct == 1) cnt++;
}
}
if(flag) printf("Cannot fall!\n");
else printf("%d\n",ans);
}//while
return 0;
}