计算机机试,王道机试指南(第二版)笔记

机试指南一些笔记。。。
题目代码github链接
https://github.com/BenedictYoung
链接

视频 and pdf
链接:https:
//pan.bai
du.
com/s/1WFl
E5hWgg
Y9c3J97
fVbsRg?pwd=
xwep
提取
码:xw
ep

文章目录

  • 计算机机试,王道机试指南(第二版)笔记
  • 第六章 数学问题
    • 6.1 进制转换
      • 例题 6.1 二进制数(北京邮电大学复试上机题)
      • 例题 6.1 八进制(任意十进制内)
      • 例题 6.1 通用10以上进制
      • 习题 6.3 进制转换(从M进制转换为十进制通用代码)
      • 例题 6.4 进制转换2
      • 例题 6.1 进制转换(清华大学复试上机题)
      • 习题 6.2 又一版 A+B
      • 习题6.4 数制转换
    • 6.2 GCD&LCM(最大公约数And最小公倍数)
      • 例题 6.5 最大公约数
      • 例题 6.6 最小公倍数
      • 习题 6.5 最简真分数
    • 6.3 质数(Prime Number)
      • 例题 6.7 素数判定
      • 例题 6.2.3.2 Prime Number
      • 例题 6.2.3.3 素数
      • 例题 6.2.3.2 质因数的个数
      • 例题 6.2.3.3 约数的个数
      • 例题 6.2.3.2 整除问题
    • 6.4 快速幂运算
      • 6.2.3.1 快速幂
    • 6.4 矩阵
      • 6.4.1 计算两个矩阵的乘积
      • 6.4.1 矩阵幂
      • 6.4.3 A+B for Matrices
      • 6.4.3 习题 6.11 递归数列
    • 6.6 高精度整数
      • 例题6.13 a+b
      • 例题6.14 N的阶乘
      • 习题6.13 大整数的因子
  • 第七章 贪心策略
    • 7.1 简单贪心
      • 例题7.1 鸡兔同笼
      • 例题7.2 FatMouse' Trade
      • 例题7.3 Senior's Gun
      • 例题7.3 箱子打包
      • 习题 7.1 代理服务器
      • 例题7.4 Aggressive cows
      • 例题7.5 Drying
      • 习题 7.1 代理服务器(清华大学复试上机器)
    • 7.2 区间贪心
      • 7.1.1 例题7.4 今年暑假不AC
      • 7.1.1 POJ 1328 Radar Installation
      • 习题 7.2 To Fill or Not to Fill(浙江大学复试上机题)
  • 第八章 递归与分治
    • 8.1 递归策略
      • 8.1.1 n的阶乘
      • 8.1.2 汉诺塔
      • 习题 8.1 杨辉三角形 略
      • 习题 8.2 全排列(北京大学复试上机题) (Ex:全排列函数 + auto)
    • 8.2 分治法
      • 8.2.1 Fibonacci
      • 8.2.2 二叉树
      • 习题 8.3 2的幂次方 (上海交通大学复试上机题)
  • 第九章 搜索
    • 9.1 BFS
      • 例题 9.1.1 Catch That Cow
      • 例题 9.1.2 Find The Multiple
      • 小结
      • 习题 9.1 玛雅人的密码(清华大学复试上机题)
    • 9.2 DFS(深度优先遍历)
      • 例题 9.3 A Knight's Journey
      • 例题 9.4 Square
      • 习题 9.2.1 八皇后
  • 第十章 数据结构二
    • 10.1 二叉树
      • 10.1.1 例10.1 二叉树遍历(清华大学复试上机题)
      • 10.1.2 例10.2 二叉树遍历(华中科技大学复试上机题)
    • 10.2 二叉排序树
      • 例题10.3 二叉排序树(华中科技大学复试上机题)
      • 例题10.4 二叉排序树(华中科技大学复试上机题)
      • 习题 10.1 二叉搜索树
    • 10.3 优先队列
      • 例题10.5 复数集合
      • 例题10.6 哈夫曼树
      • 习题 10.3 查找第K小的数(北京邮电大学复试上机题)
      • 习题 10.3 搬水果(吉林大学复试上机题)
    • 10.4 散列表(map,unordered_map)
      • 10.4.1 例题10.7 查找学生信息
      • 10.4.2 例题10.8 魔咒词典(浙江大学复试上机题)
      • 10.4.3 例题10.8 子串计算(北京大学复试上机题)
      • 10.4.4 习题 10.4 统计同成绩学生人数(浙江大学复试上机题)
      • 10.4.5 习题 10.5 开门人和关门人(浙江大学复试上机题)
      • 10.4.6 习题 10.6 谁是你的潜在朋友(北京大学复试上机题)
  • 第十一章 图论问题
    • 11.1 概述
    • 11.2 并查集
      • 11.2.1 例题11.2 连通图
      • 11.2.2 例题11.1 畅通工程(浙江大学上机题)
      • 11.2.3 例题11.3 Is It A Tree(北京大学上机题)
      • 11.2.4 习题11.1 找出直系亲属(浙江大学复试上机题)
      • 11.2.5 习题11.2 第一题(上海交通大学复试上机题)
      • 11.2.6 习题11.3 Head of a Gang(浙江大学复试上机题)
    • 11.3 最小生成树
      • 11.3.1 例题11.4 还是畅通工程
      • 11.3.3 例题11.5 继续畅通工程
      • 11.3.4 习题11.4 Freckles
    • 11.4 最短路径
      • 11.4.1 畅通工程续
      • 11.4.2 例题11.7 最短路径问题
      • 11.4.3 习题11.6 最短路径(上海交通大学复试上机题)
      • 11.4.4 习题11.7 I Wanna Go Homw(北京大学复试上机题)
    • 11.5 拓扑排序
      • 11.5.1 例题 11.9 确定比赛名次
      • 11.5.2 例题 11.8 Legal or Not(拓扑排序判环)
    • 11.6 关键路径
      • 11.6.1 例题11.10 Instructions Arrangement
    • 11.7 小结

第六章 数学问题

6.1 进制转换

例题 6.1 二进制数(北京邮电大学复试上机题)

链接:https://www.nowcoder.com/practice/103dd589fed14457a673c613d8de3841?tpId=40&tqId=21519&rp=1&ru=/ta/kaoyan&qru=/ta/kaoyan&difficulty=&judgeStatus=&tags=/question-ranking

· 描述

大家都知道,数据在计算机里中存储是以二进制的形式存储的。 有一天,小明学了C语言之后,他想知道一个类型为unsigned int 类型的数字,存储在计算机中的二进制串是什么样子的。 你能帮帮小明吗?并且,小明不想要二进制串中前面的没有意义的0串,即要去掉前导0。

· 输入描述:

多行,每一行表示要求的数字

· 输出描述:

输出共T行。每行输出求得的二进制串。

· 示例1

· 输入:
23
535
2624
56275
989835

· 输出:
10111
1000010111
101001000000
1101101111010011
11110001101010001011

· 分析

本题是一道十进制转换成二进制数的经典题目,主需要将数字n不断进行对2取模和对2整除运算,便可以得到各个数位的值。但是要注意,这种方法求出的数位顺序是从b0到bn,而数字输出需要的数位顺序是从bn到b0,因此在求出各个数位的值后,要将结果逆序输出。

代码如下(示例):

#include <iostream>
#include <cstdio>
#include <vector>using namespace std;int main(){unsigned int n;while(scanf("%d",&n)!=EOF){vector<int> binary;while(n!=0){binary.push_back(n%2);n/=2;}for(int i = binary.size()-1;i>=0;--i){//逆序输出printf("%d",binary[i]);}printf("\n");}return 0;
}

例题 6.1 八进制(任意十进制内)

链接:https://www.nowcoder.com/practice/eda051c1effc4dffa630bc8507f0c5f7?tpId=40&tqId=21562&rp=1&ru=/ta/kaoyan&qru=/ta/kaoyan&difficulty=&judgeStatus=&tags=/question-ranking

· 描述

输入一个整数,将其转换成八进制数输出。

· 输入描述:

输入包括一个整数N(0<=N<=100000)。

· 输出描述:

可能有多组测试数据,对于每组数据, 输出N的八进制表示数。

示例1
输入:
7
8
9

输出:
7
10
11

分析:将上述代码稍加修改,将2改为10以内任意进制参数即可。

代码如下(示例):

#include <iostream>
#include <cstdio>
#include <vector>using namespace std;void ConvertT2N(int number,int n){//转换成为任意进制数vector<int> answer;if(number==0){//特殊用例number=0answer.push_back(0);}else{while(number!=0){//不断对2取模,求出二进制最末尾数字,随后除2answer.push_back(number%n);number/=n;}}for(int i = answer.size()-1;i>=0;i--){//逆序输出printf("%d",answer[i]);}printf("\n");
}
int main(){int number;while(scanf("%d",&number)!=EOF){ConvertT2N(number,8);//转换为八进制}return 0;
}

例题 6.1 通用10以上进制

说明:适用于十进制转换为其他进制类型,仅需要考虑数字到字符转换,vector容器转换为char类型,其他代码类似。

//例题 6.1 通用10以上进制
#include <iostream>
#include <cstdio>
#include <vector>using namespace std;char Int2Char(int target){//数字到字符转换if(target<10){return target+'0';}else{return target-10+'A';}
}
void ConvertT2N(int number,int n){//转换成为任意进制数vector<char> answer;//改为字符vector容器if(number==0){//特殊用例number=0answer.push_back('0');}else{while(number!=0){//不断对2取模,求出二进制最末尾数字,随后除2answer.push_back(Int2Char(number%n));number/=n;}}for(int i = answer.size()-1;i>=0;i--){//逆序输出printf("%c",answer[i]);}printf("\n");
}
int main(){int number;while(scanf("%d",&number)!=EOF){ConvertT2N(number,16);//转换为十以上进制}return 0;
}

习题 6.3 进制转换(从M进制转换为十进制通用代码)

链接:https://www.nowcoder.com/practice/deb19498bc644f53a6a99905ef5ee01d?tpId=40&tqId=21411&rp=1&ru=/ta/kaoyan&qru=/ta/kaoyan&difficulty=&judgeStatus=&tags=/question-ranking

· 描述
写出一个程序,接受一个十六进制的数值字符串,输出该数值的十进制字符串(注意可能存在的一个测试用例里的多组数据)。

· 输入描述:
输入一个十六进制的数值字符串。

· 输出描述:
输出该数值的十进制字符串。

· 示例1
输入:
0xA

输出:
10

说明:与上述思路类似,将十六进制数作为string类型输入,然后同上述思路相反,采用先乘以m,在加上该位对应的int数据。

#include <iostream>
#include <cstdio>using namespace std;int Char2Int(char target){//字符转换为数字if('0'<=target&&target<='9'){return target-'0';}else{return target-'A'+10;}
}
void ConvertM2T(string str,int m){//str表示M进制数,m为几进制int number=0;for(int i=0;i<str.size();i++){number*=m;//与十进制转换为M进制相反,为不断相乘,相加number+=Char2Int(str[i]);}printf("%d\n",number);
}
int main(){string str;while(cin>>str){str=str.substr(2);//去除0xConvertM2T(str,16);}return 0;
}

例题 6.4 进制转换2

从M进制转换为N进制

链接:https://www.nowcoder.com/practice/ae4b3c4a968745618d65b866002bbd32?tpId=40&tqId=30990&rp=1&ru=/ta/kaoyan&qru=/ta/kaoyan&difficulty=&judgeStatus=&tags=/question-ranking

思路:将上述两种代码结合起来,先将M进制数字转换为十进制,再将十进制转换为N进制即可。

//从M进制转换为N进制
//例题6.4
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>using namespace std;char Int2Char(int target){//数字转换为字符if(target<10){return target+'0';}else{return target-10+'a';}
}
int Char2Int(char target){//字符转换为数字if('0'<=target&&target<='9'){return target-'0';}else{return target-'A'+10;}
}
long long ConvertM2T(string str,int m){long long number=0;for(int i=0;i<str.size();i++){number*=m;number+=Char2Int(str[i]);}return number;
}
void ConvertT2N(long long number,int n){//转换成为任意进制数vector<int> answer;if(number==0){//特殊用例number=0answer.push_back(0);}else{while(number!=0){//不断对2取模,求出二进制最末尾数字,随后除2answer.push_back(number%n);number/=n;}}for(int i = answer.size()-1;i>=0;i--){//逆序输出printf("%d",answer[i]);}printf("\n");
}
int main(){int m,n;while(scanf("%d%d",&m,&n)!=EOF){string str;cin>>str;long long number=ConvertM2T(str,m);//由于数据范围较大,采用long long类型ConvertT2N(number,n);}
}

例题 6.1 进制转换(清华大学复试上机题)

链接:https://www.nowcoder.com/practice/0337e32b1e5543a19fa380e36d9343d7?tpId=40&tqId=21332&rp=1&ru=/ta/kaoyan&qru=/ta/kaoyan&difficulty=&judgeStatus=&tags=/question-ranking

· 描述

将一个长度最多为30位数字的十进制非负整数转换为二进制数输出。

· 输入描述:

多组数据,每行为一个长度不超过30位的十进制非负整数。 (注意是10进制数字的个数可能有30个,而非30bits的整数)

· 输出描述:

每行输出对应的二进制数。

· 分析

由于输入的位数多达30位,因此采用字符串string模拟的数字不断进行对2取模和对2整除运算,即可求出结果。
对于取模运算,可以将其转换为对字符串中的最低数位进行取模运算,二者在功能上是等价的。

代码如下(示例):

#include <iostream>
#include <cstdio>
#include <string>
#include <vector>using namespace std;string Divide(string str,int x){ //字符串除法int remainder = 0;  //保留余数for(int i=0;i<str.size();i++){int current=remainder*10+str[i]-'0';str[i]=current/x+'0';remainder=current%x;}int pos=0;while(str[pos]=='0'){  //寻找首个非零下标pos++;}return str.substr(pos);  //删除前置多余的0
}
int main(){string str;while(cin>>str){vector<int> binary;while(str.size()!=0){int last=str[str.size()-1]-'0';  //最低位的值binary.push_back(last%2);  //取模运算str=Divide(str,2);  //整除运算}for(int i=binary.size()-1;i>=0;i--){printf("%d",binary[i]);}printf("\n");}return 0;
}

习题 6.2 又一版 A+B

题目链接

注意特殊用例,输入AB为0 0时候,应该结果输出0。

#include <iostream>
#include <string>
#include <cstdio>
#include <vector>using namespace std;void ToM(long long n,int m){vector <int>answer;if(n==0){answer.push_back(0);//注意0的特殊用例}while(n!=0){answer.push_back(n%m);n/=m;}for(int i=answer.size()-1;i>=0;i--){printf("%d",answer[i]);}printf("\n");
}int main(){long long m,A,B;while(cin>>m>>A>>B){if(m==0){break;}long long res=A+B;ToM(res,m);}return 0;
}

习题6.4 数制转换

题目链接

#include <iostream>
#include <string>
#include <cstdio>
#include <vector>
using namespace std;long Char2Int(char c){if(c>='0'&&c<='9'){return c-'0';}else if(c>='A'&&c<='F'){return c-'A'+10;}else{return c-'a'+10;}
}
char Int2Char(long number){if(number>=0&&number<=9){return number+'0';}else{return number-10+'A';}
}long ToD(string str,int m){//m进制转换为十进制long number=0;for(int i=0;i<str.size();i++){number*=m;number+=Char2Int(str[i]);}return number;
}void ToM(long num,int m){//十进制转换为m进制vector<char> res;if(num==0){res.push_back('0');}while(num!=0){res.push_back(Int2Char(num%m));num/=m;}for(int i=res.size()-1;i>=0;i--){printf("%c",res[i]);}printf("\n");
}int main(){int a,b;string n;cin>>a>>n>>b;ToM(ToD(n,a),b);
}

6.2 GCD&LCM(最大公约数And最小公倍数)

思想:辗转相除法(欧几里得法)

例题 6.5 最大公约数

链接:https://www.nowcoder.com/practice/20216f2c84bc438eb5ef05e382536fd3?tpId=40&tqId=21492&rp=1&ru=/ta/kaoyan&qru=/ta/kaoyan&difficulty=&judgeStatus=&tags=/question-ranking

描述

输入两个正整数,求其最大公约数。

输入描述:

测试数据有多组,每组输入两个正整数。

输出描述:

对于每组输入,请输出其最大公约数。

示例1
输入:
49 14
输出:
7

#include <iostream>
#include <cstdio>using namespace std;int GCD(int a,int b){if(b==0){return a;}else{return GCD(b,a%b);}
}
int main(){int a,b;while(scanf("%d%d",&a,&b)!=EOF){printf("%d\n",GCD(a,b));}
}

例题 6.6 最小公倍数

思路:a * b /gcd(a,b)
杭电oj题,进不去。。。

#include <iostream>
#include <cstdio>using namespace std;int GCD(int a,int b){if(b==0){return a;}else{return GCD(b,a%b);}
}
int main(){int a,b;while(scanf("%d%d",&a,&b)!=EOF){printf("%d\n",a*b/GCD(a,b));//修改这里即可}
}

习题 6.5 最简真分数

题目链接

淦,别写死循环,被测试用例误导了,以为n==0跳出,以后输入测试样例还是用while(scanf()!=EOF

#include <iostream>
#include <string>
#include <cstdio>
#include <algorithm>using namespace std;const int MAXN=600+10;int arr[600+10];int GCD(int a,int b){if(b==0){return a;}return GCD(b,a%b);
}bool isReal(int x,int y){return GCD(x,y)==1;
}int main(){int n;while(scanf("%d",&n)!=EOF){if(n==0){break;}for(int i=0;i<n;i++){scanf("%d",&arr[i]);}int cnt=0;sort(arr,arr+n);for(int i=0;i<n;i++){for(int j=i+1;j<n;j++){if(isReal(arr[i],arr[j])){cnt++;}}}printf("%d\n",cnt);}return 0;
}

6.3 质数(Prime Number)

例题 6.7 素数判定

题目链接

说明:只需要判定到根号n为上界即可,但是注意要包括根号n判定

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;bool Judge(int n){if(n<2){return false;}int bound=sqrt(n);//上界为根号nfor(int i=2;i<=bound;i++){//注意这里要等于上界if(n%i==0){return false;}}return true;
}
int main(){int n;while(scanf("%d",&n)!=EOF){if(Judge(n)){printf("yes\n");}else{printf("no\n");}}}

例题 6.2.3.2 Prime Number

题目链接

质数筛法:不断将当前指针所指向的数字的倍数全部标记为非质数(从2开始)

#include <iostream>
#include <cstdio>
#include <vector>using namespace std;const int MAXN=1e5+10;vector<int> prime;
bool isPrime[MAXN];void Initial(){//     for(int i=0;i<MAXN;i++){//         isPrime[i]=true;
//     }
//    或者可以采用fill函数fill(isPrime,isPrime+MAXN,true);isPrime[0]=false;isPrime[1]=false;//0和1均为非质数for(int i=2;i<MAXN;i++){if(!isPrime[i]){continue;}prime.push_back(i);if(i>MAXN/i){//防止i*i出现溢出continue;}for(int j=i*i;j<MAXN;j+=i){isPrime[j]=false;}}
}
int main(){int k;Initial();while(scanf("%d",&k)!=EOF){printf("%d\n",prime[k-1]);}return 0;
}

例题 6.2.3.3 素数

题目链接

别忘了开始调用Initial()

#include <iostream>
#include <string>
#include <cstdio>
#include <vector>using namespace std;const int MAXN =1e5+10;bool isPrime[MAXN];vector <int>primes;//模板代码
void Initial(){for(int i=0;i<MAXN;i++){isPrime[i]=true;}isPrime[0]=false;isPrime[1]=false;//0,1不是质数for(int i=2;i<MAXN;i++){if(!isPrime[i]){continue;//如果当前数不是质数,略过,肯定它的倍数已经被处理过了}primes.push_back(i);for(int j=i*i;j<MAXN;j+=i){//注意这里可以从i*i开始处理,因为i*2-i*(i-1)肯定是被处理过了isPrime[j]=false;}}return;
}
int main(){int n;scanf("%d",&n);bool flag=false;Initial();//别忘了调用for(int i=0;i<primes.size();i++){if(primes[i]<n&&primes[i]%10==1){if(!flag){flag=!flag;printf("%d",primes[i]);}else{printf(" %d",primes[i]);}}}}

例题 6.2.3.2 质因数的个数

题目链接

质因子分解:短除法

#include <iostream>
#include <cstdio>
#include <vector>using namespace std;const int MAXN=4e4;//由于数据范围较大,大于10^9,因此这里只考虑根号10^9大约4e4,如果有大于根号n的情况,那么只会有且仅有一个这个因子,最后处理一下即可,在if(number>1)处理vector<int> prime;
bool isPrime[MAXN];void Initial(){//     for(int i=0;i<MAXN;i++){//         isPrime[i]=true;
//     }
//    或者可以采用fill函数fill(isPrime,isPrime+MAXN,true);isPrime[0]=false;isPrime[1]=false;//0和1均为非质数for(int i=2;i<MAXN;i++){if(!isPrime[i]){continue;}prime.push_back(i);if(i>MAXN/i){//防止i*i出现溢出continue;}for(int j=i*i;j<MAXN;j+=i){isPrime[j]=false;}}
}
int NumberOfPrimeFactors(int number){int answer=0;for(int i=0;i<prime.size();i++){int factor=prime[i];if(number<factor){break;}int exponent=0;while(number%factor==0){exponent++;number/=factor;}answer+=exponent;}if(number>1){//!!!如果number大于1时候,说明n此时还有一个大于根号n的质因子,还需要进行累加answer+=1;}return answer;
}int main(){Initial();int number;while(scanf("%d",&number)!=EOF){printf("%d\n",NumberOfPrimeFactors(number));}
}

例题 6.2.3.3 约数的个数

题目链接



每个质因子都有ei+1种选择,因此对于每个质因子进行一种组合结果即为(e1+1)(e2+1)…(en+1)

#include <iostream>
#include <cstdio>
#include <vector>using namespace std;const int MAXN=4e4;vector<int> prime;
bool isPrime[MAXN];void Initial(){//     for(int i=0;i<MAXN;i++){//         isPrime[i]=true;
//     }
//    或者可以采用fill函数fill(isPrime,isPrime+MAXN,true);isPrime[0]=false;isPrime[1]=false;//0和1均为非质数for(int i=2;i<MAXN;i++){if(!isPrime[i]){continue;}prime.push_back(i);if(i>MAXN/i){//防止i*i出现溢出continue;}for(int j=i*i;j<MAXN;j+=i){isPrime[j]=false;}}
}
int NumberOfFactors(int number){int answer=1;for(int i=0;i<prime.size();i++){int factor=prime[i];if(number<factor){break;}int exponent=0;while(number%factor==0){exponent++;number/=factor;}answer*=(exponent+1);}if(number>1){answer*=2;}return answer;
}int main(){Initial();int n;while(scanf("%d",&n)!=EOF){if(n==0){break;}for(int i=0;i<n;i++){int number;scanf("%d",&number);printf("%d\n",NumberOfFactors(number));}}
}

例题 6.2.3.2 整除问题

题目链接
思路来自题解

#include <iostream>
#include <vector>
#include <cstring>using namespace std;const int MAXN=1000+10;int factorOfn[MAXN];//质因子出现个数
int factorOfa[MAXN];int main(){int n,a;scanf("%d %d",&n,&a);memset(factorOfa,sizeof(factorOfa),0);memset(factorOfn,sizeof(factorOfn),0);for(int i=2;i<=n;i++){//对于2-n中每个数字进行处理(n!)int tmp=i;//存储一下ifor(int j=2;j*j<=i;j++){//找出质因子while(i%j==0){//每个质因子进行计数factorOfn[j]++;i/=j;}}if(i>1){//剩下质因子factorOfn[i]++;}i=tmp;}//求解质因子情况代码模板for(int i=2;i*i<=a;i++){while(a%i==0){factorOfa[i]++;a/=i;}}if(a>1){factorOfa[a]++;}int k=1e6;for(int i=0;i<MAXN;i++){if(factorOfa[i]){k=min(k,factorOfn[i]/factorOfa[i]);}}printf("%d",k);
}

6.4 快速幂运算

6.2.3.1 快速幂


模板代码

int QuickPower(int x,int n){//求x的n次幂(模板)int answer=1;while(n!=0){if(n%2==1){answer*=x;}n/=2;x*=x;}return answer;
}

杭电oj 2035题
Tip:需要进行模运算处理

#include <iostream>
#include <cstdio>using namespace std;int QuickPower(int x,int n){//求x的n次幂(模板)int mod=1000;int answer=1;while(n!=0){if(n%2==1){answer*=x;answer%=mod;}n/=2;x*=x;x%=mod;}return answer;
}
int main(){int a,b;while(scanf("%d%d",&a,&b)!=EOF){if(a==0&&b==0){break;}printf("%d\n",QuickPower(a, b));}return 0;
}

6.4 矩阵

6.4.1 计算两个矩阵的乘积

题目链接

#include <iostream>
#include <cstdio>using namespace std;const int MAXN=100;//用二维数组模拟矩阵
//矩阵定义
struct Matrix{int row,col;int matrix[MAXN][MAXN];Matrix(){}Matrix(int r,int c):row(r),col(c){}
};//矩阵相加
Matrix Add(Matrix x,Matrix y){Matrix answer=Matrix(x.row,x.col);for(int i=0;i<answer.row;i++){for(int j=0;j<answer.col;j++){answer.matrix[i][j]=x.matrix[i][j]+y.matrix[i][j];}}return answer;
}//矩阵相乘
Matrix Multiply(Matrix x,Matrix y){Matrix answer=Matrix(x.row,y.col);for(int i=0;i<answer.row;i++){for(int j=0;j<answer.col;j++){answer.matrix[i][j]=0;for(int k=0;k<x.col;k++){answer.matrix[i][j]+=x.matrix[i][k]*y.matrix[k][j];}}}return answer;
}//矩阵转置
Matrix Transpose(Matrix x){Matrix answer=Matrix(x.col,x.row);for(int i=0;i<x.row;i++){for(int j=0;j<x.col;j++){answer.matrix[j][i]=x.matrix[i][j];}}return answer;
}//矩阵求幂,采用快速幂运算
Matrix QuickPower(Matrix x,int n){Matrix answer=Matrix(x.row,x.col);for(int i=0;i<x.row;i++){for(int j=0;j<x.col;j++){if(i==j){answer.matrix[i][j]=1;}else{answer.matrix[i][j]=0;}}}while(n!=0){if(n%2==1){answer=Multiply(answer,x);}n/=2;x=Multiply(x,x);}return answer;
}//矩阵输入
void InputMatrix(Matrix& x){for(int i=0;i<x.row;i++){for(int j=0;j<x.col;j++){scanf("%d",&x.matrix[i][j]);}}return;
}//矩阵输出
void OutputMatrix(Matrix x){for(int i=0;i<x.row;i++){for(int j=0;j<x.col;j++){if(j==0){printf("%d",x.matrix[i][j]);}else{printf(" %d",x.matrix[i][j]);}}printf("\n");}return;
}int main(){Matrix x(2,3);Matrix y(3,2);InputMatrix(x);InputMatrix(y);Matrix answer=Multiply(x,y);OutputMatrix(answer);return 0;
}

6.4.1 矩阵幂

#include <iostream>
#include <cstdio>using namespace std;const int MAXN=100;//用二维数组模拟矩阵
//矩阵定义
struct Matrix{int row,col;int matrix[MAXN][MAXN];Matrix(){}Matrix(int r,int c):row(r),col(c){}
};//矩阵相加
Matrix Add(Matrix x,Matrix y){Matrix answer=Matrix(x.row,x.col);for(int i=0;i<answer.row;i++){for(int j=0;j<answer.col;j++){answer.matrix[i][j]=x.matrix[i][j]+y.matrix[i][j];}}return answer;
}//矩阵相乘
Matrix Multiply(Matrix x,Matrix y){Matrix answer=Matrix(x.row,y.col);for(int i=0;i<answer.row;i++){for(int j=0;j<answer.col;j++){answer.matrix[i][j]=0;for(int k=0;k<x.col;k++){answer.matrix[i][j]+=x.matrix[i][k]*y.matrix[k][j];}}}return answer;
}//矩阵转置
Matrix Transpose(Matrix x){Matrix answer=Matrix(x.col,x.row);for(int i=0;i<x.row;i++){for(int j=0;j<x.col;j++){answer.matrix[j][i]=x.matrix[i][j];}}return answer;
}//矩阵求幂,采用快速幂运算
Matrix QuickPower(Matrix x,int n){Matrix answer=Matrix(x.row,x.col);for(int i=0;i<x.row;i++){for(int j=0;j<x.col;j++){if(i==j){answer.matrix[i][j]=1;}else{answer.matrix[i][j]=0;}}}while(n!=0){if(n%2==1){answer=Multiply(answer,x);}n/=2;x=Multiply(x,x);}return answer;
}//矩阵输入
void InputMatrix(Matrix& x){for(int i=0;i<x.row;i++){for(int j=0;j<x.col;j++){scanf("%d",&x.matrix[i][j]);}}return;
}//矩阵输出
void OutputMatrix(Matrix x){for(int i=0;i<x.row;i++){for(int j=0;j<x.col;j++){if(j==0){printf("%d",x.matrix[i][j]);}else{printf(" %d",x.matrix[i][j]);}}printf("\n");}return;
}int main(){int n,k;while(scanf("%d%d",&n,&k)!=EOF){Matrix x=Matrix(n,n);InputMatrix(x);Matrix answer=QuickPower(x,k);OutputMatrix(answer);}return 0;
}

6.4.3 A+B for Matrices

链接

#include <iostream>
#include <string>using namespace std;const int MAXN=10+5;struct Matrix{int row,col;int element[MAXN][MAXN];Matrix(){}Matrix(int x,int y):row(x),col(y){}
};Matrix add(Matrix A,Matrix B){Matrix answer=Matrix(A.row,A.col);for(int i=0;i<A.row;i++){for(int j=0;j<A.col;j++){answer.element[i][j]=A.element[i][j]+B.element[i][j];}}return answer;
}void input(Matrix& A){for(int i=0;i<A.row;i++){for(int j=0;j<A.col;j++){scanf("%d",&A.element[i][j]);}}
}int count(Matrix A){int cnt=0;for(int i=0;i<A.row;i++){bool flag = false;for(int j=0;j<A.col;j++){if(A.element[i][j]!=0){flag = true;break;}}if(!flag){cnt++;}}for(int i=0;i<A.col;i++){bool flag = false;for(int j=0;j<A.row;j++){if(A.element[j][i]!=0){flag = true;break;}}if(!flag){cnt++;}}return cnt;
}int main(){int m,n;while(scanf("%d",&m)!=EOF){scanf("%d",&n);if(m==0){break;}Matrix A,B;A.row=B.row=m;A.col=B.col=n;input(A);input(B);Matrix C=add(A,B);printf("%d\n",count(C));}return 0;
}

6.4.3 习题 6.11 递归数列

题目链接

调试一下午。。

  1. 注意矩阵快速幂代码书写
Matrix QuickExpon(Matrix x,int k){Matrix answer;//注意这里要初始化成单位矩阵!!!!for(int i=0;i<2;i++){for(int j=0;j<2;j++){if(i==j){answer.element[i][j]=1;}else{answer.element[i][j]=0;}}}while(k){//这里不要搞错if(k%2){answer=Multiply(answer,x);}k/=2;x=Multiply(x,x);}return answer;
}
  1. 对于int类型输入用%d,long long输入采用%lld,对应输出同样
  2. 对于结果要取模,在运算过程中都要进行取模操作

思路:采用矩阵快速幂运算。详细问题可以参考
https://blog.csdn.net/csyifanZhang/article/details/104594647

AC代码:

#include <iostream>
#include <string>using namespace std;const int MAXN=2;
const int MOD=10000;typedef long long ll;struct Matrix{ll element[2][2];Matrix(){}
};Matrix Multiply(Matrix x,Matrix y){Matrix answer;for(int i=0;i<2;i++){for(int j=0;j<2;j++){ll sum=0;for(int k=0;k<2;k++){sum=(sum+x.element[i][k]*y.element[k][j])%MOD;}answer.element[i][j]=sum;}}return answer;
}Matrix QuickExpon(Matrix x,int k){Matrix answer;for(int i=0;i<2;i++){for(int j=0;j<2;j++){if(i==j){answer.element[i][j]=1;}else{answer.element[i][j]=0;}}}while(k){if(k%2){answer=Multiply(answer,x);}k/=2;x=Multiply(x,x);}return answer;
}void input(Matrix &x,ll a,int b,int c,int d){x.element[0][0]=a;x.element[0][1]=b;x.element[1][0]=c;x.element[1][1]=d;
}int main(){int a0,a1,p,q,k;scanf("%d %d %d %d %d",&a0,&a1,&p,&q,&k);Matrix answer,pq,start;input(pq,p,q,1,0);input(start,a1,0,a0,0);answer=Multiply(QuickExpon(pq,k-1),start);printf("%lld",answer.element[0][0]%MOD);return 0;
}

6.6 高精度整数

数字范围

例题6.13 a+b

JAVA

import java.util.Scanner;
import java.math.BigInteger;public class Main{public static void main(String[] args){Scanner input=new Scanner(System.in);while(input.hasNext()){int n=input.nextInt();BigInteger answer=input.nextBigInteger();BigInteger b=input.nextBigInteger();System.out.println(a.add(b));}}
}

cpp 采用字符串string处理

#include <iostream>
#include <string>
#include <cstdio>using namespace std;string add(string a,string b){string answer;int carry=0;while(a.length()<b.length()){a.insert(0,1,'0');}while(a.length()>b.length()){b.insert(0,1,'0');}for(int i=a.length()-1;i>=0;i--){int r=(a[i]-'0')+(b[i]-'0')+carry;carry=r/10;char c=r%10+'0';answer.insert(0,1,c);}if(carry){answer.insert(0,1,'1');}return answer;
}
int main(){string a,b;while(cin>>a>>b){cout<<add(a,b)<<endl;}
}

cpp 结构体,数字数组进行处理

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>using namespace std;const int MAXN=10000;struct BigInteger{int digit[MAXN];int length;BigInteger();BigInteger operator=(string str);BigInteger operator+(const BigInteger& b);friend istream& operator>>(istream& in,BigInteger& x);friend ostream& operator<<(ostream& out,const BigInteger& x);
};istream& operator>>(istream&in,BigInteger &x){string str;in >> str;x = str;return in;
}ostream& operator<<(ostream& out,const BigInteger& x){for(int i=x.length-1;i>=0;i--){out<<x.digit[i];}return out;
}BigInteger::BigInteger(){memset(digit,0,sizeof(digit));length=0;
}BigInteger BigInteger::operator=(string str){memset(digit,0,sizeof(digit));length=str.size();for(int i=0;i<length;i++){digit[i]=str[length-i-1]-'0';}return *this;
}//template
BigInteger BigInteger::operator+(const BigInteger& b){BigInteger answer;int carry=0;for(int i=0;i<length||i<b.length;i++){int current=carry+digit[i]+b.digit[i];carry=current/10;answer.digit[answer.length++]=current%10;}if(carry!=0){answer.digit[answer.length++]=carry;}return answer;}int main(){BigInteger a;BigInteger b;while(cin>>a>>b){cout<<a+b<<endl;}return 0;
}

例题6.14 N的阶乘

习题6.13 大整数的因子

题目链接

大整数除法代码

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
//本题是大整数除法
int divideString(string a,int x)
{int remainder=0;for(int i=0;i<a.size();i++){int num=remainder*10+a[i]-'0';a[i]=num/x+'0';remainder=num%x;}return remainder;//求商返回a,求模返回remainder
}
using namespace std;
int main()
{string c;while(cin>>c){if(c=="-1")break;int sum=0;for(int i=2;i<10;i++){if(divideString(c,i)==0){printf("%d ",i);sum++;}}if(!sum)printf("none\n");else printf("\n");        }return 0;
}

第七章 贪心策略

贪心策略常用于求解最优化问题,其核心思想是,总是选择当前状态下最优的策略,也就说,它并不以整体最优进行考虑,而只考虑当前这一步。因此,这种方法能够得到局部最优解,在最后往往也能够获得全局最优解,但并不保证收敛到全局最优解。

7.1 简单贪心

例题7.1 鸡兔同笼

题目链接

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;int main(){int n;while(scanf("%d",&n)!=EOF){if(n%2){printf("0 0\n");}else{if(n%4==0){printf("%d %d\n",n/4,n/2);}else{printf("%d %d\n",n/4+1,n/2);}}}return 0;
}

例题7.2 FatMouse’ Trade

题目来源:杭电oj - 1009
Tip:
尽可能多的咖啡豆:贪心策略
注意:商品可以任意切分,无需买完

#include <iostream>
#include <cstdio>
#include <algorithm>using namespace std;const int MAXN=1000;struct JavaBean{double weight;double cost;
};JavaBean arr[MAXN];bool Compare(JavaBean x,JavaBean y){//比较性价比return x.weight/x.cost>y.weight/y.cost;
}int main(){int n,m;while(scanf("%d%d",&m,&n)!=EOF){if(n==-1&&m==-1){break;}for(int i=0;i<n;i++){scanf("%lf%lf",&arr[i].weight,&arr[i].cost);}sort(arr,arr+n,Compare);double answer=0;for(int i=0;i<n;i++){if(m>=arr[i].cost){m-=arr[i].cost;answer+=arr[i].weight;}else{answer+=arr[i].weight*(m/arr[i].cost);m=0;break;}}printf("%.3f",answer);}return 0;
}

例题7.3 Senior’s Gun

杭电oj - 5281

#include <iostream>
#include <cstdio>
#include <algorithm>using namespace std;const int MAXN=100000+10;long long gun[MAXN];
long long monster[MAXN];bool Compare(long long x,long long y){return x>y;
}int main(){int caseNumber;scanf("%d",&caseNumber);while(caseNumber--){int n,m;scanf("%d %d",&n,&m);for(int i=0;i<n;i++){scanf("%lld",&gun[i]);}for(int i=0;i<m;i++){scanf("%lld",&monster[i]);}sort(gun,gun+n,Compare);sort(monster,monster+m);long long answer=0;for(int i=0;i<n;i++){if(i>=m||gun[i]<=monster[i]){break;}answer+=gun[i]-monster[i];}printf("%lld",answer);}
}

例题7.3 箱子打包

ECNU 1045题
题目链接

#include <iostream>
#include <cstdio>
#include <algorithm>using namespace std;const int MAXN=1e5+10;int length[MAXN];int main(){int n,l;scanf("%d%d",&n,&l);for(int i=0;i<n;i++){scanf("%d",&length[i]);}sort(length,length+n);int left=0;int right=n-1;int answer=0;while(left<=right){if(length[left]+length[right]<=l){left++;right--;}else{right--;}answer++;}printf("%d",answer);return 0;
}

习题 7.1 代理服务器

题目链接

思路:贪心(来自题解):
依次访问服务器servers,遇到代理则标记一下,
直到遇到一个代理A时全部代理都被标记过,这时认为之前都是用这个代理A进行访问的,
此时切换代理(次数+1)并清零A以外的标记,同样的,遇到代理标记一下,
直到遇到一个代理B时全部代理都被标记过,这时认为之前都是用这个代理B进行访问的,
切换代理(次数+1),以此类推……

#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <algorithm>using namespace std;const int MAXN=1000+10;vector<string> proxy;
vector<string> server;
int flag[MAXN];int main(){memset(flag, 0, sizeof(flag));int n,m;scanf("%d",&n);for(int i=0;i<n;i++){string str;cin>>str;proxy.push_back(str);}scanf("%d",&m);int answer=0;int index=0;int left=n;//剩余未被标记服务器数目for(int i=0;i<m;i++){string str;cin>>str;auto it=find(proxy.begin(),proxy.end(),str);if(it!=proxy.end()){//这是在vector中返回迭代器所指向的元素的下标的常用手段//使用当前迭代器减去起始迭代器//即是当前迭代器所指向的元素的下标index=it-proxy.begin();if(!flag[index]){flag[index]=1;left--;}}if(left==0){answer++;if(n==1){printf("-1");return 0;}memset(flag, 0, sizeof(flag));//最后一个被标记的代理服务器不能参与下次循环flag[index] = 1;left = n - 1;}}printf("%d",answer);return 0;
}

例题7.4 Aggressive cows

POJ 2456
题目链接

Tip:
最大的最小间距(类似的问题):二分策略

#include <cstdio>
#include <iosteam>
#include <algorithm>using namespace std;const int MAXN=1e5+10;int arr[MAXN];bool Judge(int n,int m,int distance){int current=arr[0];int number=1;for(int i=1;i<n;i++){if(arr[i]-current>=distance){number++;current=arr[i];}if(number>=m){return true;}}return false;
}int main(){int n,m;while(scanf("%d%d",&n,&m)!=EOF){for(int i=0;i<n;i++){scanf("%d",&arr[i]);}sort(arr,arr+n);int left=1;int right=arr[n-1]-arr[0];while(left<=right){int middle=left+(right-left)/2;if(Judge(n,m,middle)){left=middle+1;}else{right=middle-1;}}printf("%d\n",right);}return 0;
}

例题7.5 Drying

POJ 3104
题目链接


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>using namespace std;const int MAXN=1e5+10;int water[MAXN];bool Judge(int n,int k,int time){int sum=0;for(int i=0;i<n;i++){if(water[i]>time){sum+=ceil((water[i]-time)*1.0/(k-1);//注意这里是k-1因为与此同时风干速度也是1//ceil为取上整数函数,相对应floor为取下整数,位于cmath中}if(sum>time){return false;}}return true;
}int main(){int n;scanf("%d",&n);for(int i=0;i<n;i++){scanf("%d",&water[i]);}int k;scanf("%d",&k);sort(water,water+n);if(k==1){//烘干机能力为1时候,与空气无异,取决于最大水分那件衣服printf("%d\n",water[n-1]);}else{int left=1;int right=arr[n-1];while(left<=right){int middle=left+(right-left)/2;if(Judge(n,k,middle)){right=middle-1;}else{left=middle+1;}}printf("%d\n",left);}return 0;
}

习题 7.1 代理服务器(清华大学复试上机器)

题目链接

思路:贪心:
依次访问服务器servers,遇到代理则标记一下,
直到遇到一个代理A时全部代理都被标记过,这时认为之前都是用这个代理A进行访问的,
此时切换代理(次数+1)并清零A以外的标记,同样的,遇到代理标记一下,
直到遇到一个代理B时全部代理都被标记过,这时认为之前都是用这个代理B进行访问的,
切换代理(次数+1),以此类推……

#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <algorithm>using namespace std;const int MAXN=1000+10;vector<string> proxy;
vector<string> server;
int flag[MAXN];int main(){memset(flag, 0, sizeof(flag));int n,m;scanf("%d",&n);for(int i=0;i<n;i++){string str;cin>>str;proxy.push_back(str);}scanf("%d",&m);int answer=0;int index=0;int left=n;//剩余未被标记服务器数目for(int i=0;i<m;i++){string str;cin>>str;auto it=find(proxy.begin(),proxy.end(),str);if(it!=proxy.end()){//这是在vector中返回迭代器所指向的元素的下标的常用手段//使用当前迭代器减去起始迭代器//即是当前迭代器所指向的元素的下标index=it-proxy.begin();if(!flag[index]){flag[index]=1;left--;}}if(left==0){answer++;if(n==1){printf("-1");return 0;}memset(flag, 0, sizeof(flag));//最后一个被标记的代理服务器不能参与下次循环flag[index] = 1;left = n - 1;}}printf("%d",answer);return 0;
}

7.2 区间贪心

7.1.1 例题7.4 今年暑假不AC

杭电oj - 2037
思路:结束时间最早的。

#include <iostream>
#include <cstdio>
#include <algorithm>using namespace std;const int MAXN=100+10;struct program{int startTime;int endTime;
};bool Compare(program x,program y){return x.endTime<y.endTime;
}program arr[MAXN];int main(){int n;while(scanf("%d",&n)!=EOF){if(n==0){break;}for(int i=0;i<n;i++){scanf("%d%d",&arr[i].startTime,&arr[i].endTime);sort(arr,arr+n,Compare);int current=0;int answer=0;for(int i=0;i<n;i++){if(current<=arr[i].startTime){current=arr[i].endTime;answer++;}}}printf("%d",answer);}return 0;
}

7.1.1 POJ 1328 Radar Installation

题目链接

思路:从岛屿出发分析,找到可以覆盖岛屿的区间。

思路:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>using namespace std;const int MAXN=1000+10;struct Interval{double left;double right;
};Interval arr[MAXN];bool Compare(Interval a,Interval b){return a.left<b.left;
}int main(){int n,d;int caseNumber=0;while(scanf("%d%d",&n,&d)!=EOF){if(n==0&&d==0){break;}bool flag=true;//标记是否可以实现for(int i=0;i<n;i++){int x,y;scanf("%d%d",&x,&y);if(y>d){flag=false;}else{arr[i].left=x-sqrt(d*d-1.0*y*y);arr[i].right=x+sqrt(d*d-1.0*y*y);}}if(!flag){printf("Case %d: %d\n",++caseNumber,-1);}else{sort(arr,arr+n,Compare);double current=arr[0].right;int answer=1;for(int i=1;i<n;i++){if(arr[i].left<=current){current=min(current,arr[i].right);}else{current=arr[i].right;answer++;}}printf("Case %d: %d\n",++caseNumber,answer);}}return 0;
}

习题 7.2 To Fill or Not to Fill(浙江大学复试上机题)

题目链接
摆烂了。。太菜了我

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 510;
const int inf = 0x3fffffff;struct station{double price,dis;    //价格、与起点的距离
}st[maxn];bool cmp(station a,station b){return a.dis < b.dis;    //按距离从小到大排序
}int main(){int n;double Cmax,D,Davg;while(scanf("%lf%lf%lf%d",&Cmax,&D,&Davg,&n)!=EOF){for(int i=0;i<n;i++){scanf("%lf%lf",&st[i].price,&st[i].dis);}st[n].price = 0;  //数组最后面放置终点,价格为0st[n].dis = D;      //终点距离为Dsort(st,st+n,cmp); //将所有加油站按距离从小到大排序if(st[0].dis != 0){   //如果排序后的第一个加油站距离不是0,说明无法前进 printf("The maximum travel distance = 0.00\n"); }else{int now=0;  //当前所处的加油站编号//总花费、当前油量及满油行驶距离double ans=0,nowTank=0,MAX=Cmax*Davg;while(now<n){   //每次循环将选出下一个需要到达的加油站 //选出从当前加油站满油能够到达范围内的一个油价低于当前油价的加油站//如果没有低于当前油价的加油站,则选择价格最低的那个int k=-1; //最低油价的加油站的编号double priceMin = inf;for(int i=now+1;i<=n&&st[i].dis-st[now].dis<=MAX;i++){if(st[i].price < priceMin){priceMin = st[i].price;        //如果油价比当前最低油价低 k=i;    //更新最低油价//如果找到第一个油价低于当前油价的加油站,直接中断循环if(priceMin < st[now].price){break;} }}if(k == -1) break;   //无法找到加油站//下面为能找到可到达的加油站k,计算转移花费//need为从now到k需要的油量double need = (st[k].dis - st[now].dis) / Davg;if(priceMin < st[now].price){//只买足够到达加油站k的值if(nowTank < need){ans += (need-nowTank) * st[now].price;nowTank = 0; }else{nowTank -= need;} }else{  //注意这里的加满操作 ans += (Cmax-nowTank)*st[now].price;nowTank = Cmax-need;}now = k;   //到达加油站k,进入下一个循环 }if(now == n){printf("%.2f\n",ans);}else{printf("The maximum travel distance = %.2f\n",st[now].dis+MAX);} }}return 0;
} 

第八章 递归与分治

8.1 递归策略

8.1.1 n的阶乘

题目链接

#include <iostream>
#include <cstdio>using namespace std;long long Factorial(int n){if(n==0){return 1;}else{return n*Factorial(n-1);}
}
int main(){int n;while(scanf("%d",&n)!=EOF){printf("%lld\n",Factorial(n));}return 0;
}

8.1.2 汉诺塔

基础汉诺塔采用递归实现,实则考试时可以直接推出公式,无需采用递归,f(n)=2^n-1

#include <iostream>
#include <cstdio>using namespace std;long long Hanoi(int n){if(n==1){return 1;}else{return 2*Hanoi(n-1)+1;}
}int main(){int n;while(scanf("%d",&n)!=EOF){printf("%lld\n",Hanoi(n));}return 0;
}

杭电oj -2064
汉诺塔③

//递归方式或者推导公式3^n-1
#include <iostream>
#include <cstdio>using namespace std;long long Hanoi(int n){if(n==1){return 2;}else{return 3*Hanoi(n-1)+2;}
}int main(){int n;while(scanf("%d",&n)!=EOF){printf("%lld\n",Hanoi(n));}return 0;
}

习题 8.1 杨辉三角形 略

习题 8.2 全排列(北京大学复试上机题) (Ex:全排列函数 + auto)

题目链接

//我的代码,菜鸡代码
#include <iostream>
#include <set>
#include <string>
#include <algorithm>using namespace  std;char mystr[30];int n;//字母的数量set <char> sc;void print(){for(int i=0;i<n;i++){printf("%c",mystr[i]);}printf("\n");
}
void dealChar(int t){//决定下标为t的字符是哪一个if(t==n){print();}for(auto it=sc.begin();it!=sc.end();it++){char c=*it;mystr[t]=c;sc.erase(c);dealChar(t+1);sc.insert(c);}
}int main(){string str;cin>>str;n=str.size();for(int i=0;i<n;i++){sc.insert(str[i]);}dealChar(0);return 0;
}

大佬的代码
法一:利用next_permutation(s.begin(), s.end())函数法

//递归——全排列,调用
#include <iostream>
#include <algorithm>
#include <string>using namespace std;int main()
{string str;cin >> str;sort(str.begin(),str.end());cout<<str<<endl;while (next_permutation(str.begin(), str.end()))cout << str << endl;return 0;
}

方法二:利用递归的办法

#include<iostream>
#include<vector>
#include<algorithm>using namespace std;void backtrace(vector<string>& ans, string s, int start, int len){if(start == len){ans.push_back(s);return;}for(int i = start; i < len; i ++){swap(s[i], s[start]);backtrace(ans, s, start + 1, len);}
}
int main(){string s;while(cin >> s){vector<string> ans;backtrace(ans, s, 0, s.size());sort(ans.begin(), ans.end());for(string c : ans)cout << c << endl;}return 0;
}

小结:

  1. STL:set集合,存入会自动进行排序,insert添加元素,erase删除元素
  2. auto的原理就是根据后面的值,来自己推测前面的类型是什么。auto的作用就是为了简化变量初始化,如果这个变量有一个很长很长的初始化类型,就可以用auto代替。
std::vector<std::string> ve;
std::vector<std::string>::iterator it = ve.begin();
//可以替换为auto
auto it = ve.begin();
  1. next_premutation()函数详细:https://blog.csdn.net/ACdreamers/article/details/8544505
  1. ext_permutation的函数声明:#include
    bool next_permutation( iterator start, iterator end);
    next_permutation函数的返回值是布尔类型,在STL中还有perv_permutation()函数,排序方式见上面代码。注意先输出一次str
  1. next_permutation()函数功能是输出所有比当前排列大的排列,顺序是从小到大。而prev_permutation()函数功能是输出所有比当前排列小的排列,顺序是从大到小。

8.2 分治法

分而治之

8.2.1 Fibonacci

题目链接
方法选择

分治法

#include <iostream>
#include <cstdio>using namespace std;int Fibonacci(int n){if(n==1||n==0){//递归出口return n;}else{return Fibonacci(n-1)+Fibonacci(n-2);}
}
int main(){int n;while(scanf("%d",&n)!=EOF){printf("%d\n",Fibonacci(n));}return 0;
}

递推法

#include <iostream>
#include <cstdio>using namespace std;const int MAXN=30+10;int fibonacci[MAXN];void Initial(){fibonacci[0]=0;fibonacci[1]=1;for(int i=2;i<MAXN;i++){fibonacci[i]=fibonacci[i-1]+fibonacci[i-2];}
}
int main(){int n;Initial();while(scanf("%d",&n)!=EOF){printf("%d\n",fibonacci[n]);}return 0;
}

矩阵快速幂

#include <iostream>
#include <cstdio>using namespace std;const int MAXN=100;//用二维数组模拟矩阵
//矩阵定义
struct Matrix{int row,col;int matrix[MAXN][MAXN];Matrix(){}Matrix(int r,int c):row(r),col(c){}
};//矩阵相乘
Matrix Multiply(Matrix x,Matrix y){Matrix answer=Matrix(x.row,y.col);for(int i=0;i<answer.row;i++){for(int j=0;j<answer.col;j++){answer.matrix[i][j]=0;for(int k=0;k<x.col;k++){answer.matrix[i][j]+=x.matrix[i][k]*y.matrix[k][j];}}}return answer;
}//矩阵求幂,采用快速幂运算
Matrix QuickPower(Matrix x,int n){Matrix answer=Matrix(x.row,x.col);for(int i=0;i<x.row;i++){for(int j=0;j<x.col;j++){if(i==j){answer.matrix[i][j]=1;}else{answer.matrix[i][j]=0;}}}while(n!=0){if(n%2==1){answer=Multiply(answer,x);}n/=2;x=Multiply(x,x);}return answer;
}int main(){int n;while(scanf("%d",&n)!=EOF){Matrix fibonacci=Matrix(2,2);fibonacci.matrix[0][0]=1;fibonacci.matrix[0][1]=1;fibonacci.matrix[1][0]=1;fibonacci.matrix[1][1]=0;Matrix answer=QuickPower(fibonacci,n);printf("%d",answer.matrix[0][1]);}return 0;
}

8.2.2 二叉树

题目链接

#include <iostream>
#include <cstdio>int CountNodes(int m,int n){if(m>n){return 0;}else{return CountNodes(2*m, n)+CountNodes(2*m+1,n)+1;}
}
int main(){int m,n;while(scanf("%d%d",&m,&n)!=EOF){if(m==0&&n==0){break;}printf("%d\n",CountNodes(m, n));}return 0;
}

习题 8.3 2的幂次方 (上海交通大学复试上机题)

题目链接

#include <iostream>
#include <string>using namespace std;void pre(int n){if(n==2||n==0){//递归出口printf("%d",n);return;}int ex=31;while(n){if(n&(0x80000000)){//获取当前最高位if(ex==1){//2^1单独输出2printf("2");}else{printf("2(");pre(ex);printf(")");}if((n<<1)!=0){//不是最后一个有效位,就输出加号printf("+");}}ex--;n<<=1;}
}
int main(){int n;scanf("%d",&n);pre(n);return 0;
}

第九章 搜索

搜索:

eg1: 寻找从A到E的路径:
>状态空间:所有的点
状态转移:转向邻接点
起始状态:A
目标状态:E

eg2:寻找从A到E的最低代价的路径(path from A to E with lowest cost)
状态空间(点,花费)
状态转移:转向邻接点并增加花费
起始状态:(A,0)
目标状态:(E,lowest)

搜索:起始状态经过一系列状态转换达到目标状态的过程。

计算机中常常采取搜索树解决(Search Trees)

如上述图的搜索树如下:

搜索路径的搜索树如上。


考虑代价的搜索树如上。

搜索策略:

9.1 BFS

宽度优先搜索(BFS):处理最浅的节点(逐层处理每一层的节点)

例题 9.1.1 Catch That Cow

poj - 3278
题目链接

分析:
状态空间:(位置n,时间t)
状态转移:(n-1,t+1),(n+1,t+1),(n*2,t+1)
起始状态:(N,0)
目标状态:(K,lowest time)

搜索树:

应该采用宽度优先搜索,应该一层一层进行搜索,应该在每一层查看是否有状态满足目标状态,因为每一层的借点的时间是相同的。此外,如果存在坐标点已经访问过了,那么没有必要再次浪费时间进行搜索,可以建立visit数组进行标记。

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>using namespace std;const int MAXN=1e5+10;struct Status{int position;int time;Status(){}Status(int p,int t):position(p),time(t){}
};bool visit[MAXN];int BFS(int n,int k){queue<Status> myQueue;myQueue.push(Status(n,0));visit[n]=true;while(!myQueue.empty()){Status current=myQueue.front();if(current.position==k){return current.time;}myQueue.pop();for(int i=0;i<3;i++){Status next=current;if(i==0){next.position-=1;}else if(i==1){next.position+=1;}else{next.position*=2;}next.time++;if(next.position<0||next.position>1e5||visit[next.position]){//如果位置非法,或者已经访问过(没有必要浪费时间再访问),不处理即可continue;}myQueue.push(next);visit[next.position]=true;}}return 0;
}
int main(){int n,k;scanf("%d%d",&n,&k);memset(visit,false,sizeof(visit));printf("%d\n",BFS(n,k));return 0;
}

例题 9.1.2 Find The Multiple

poj - 1426
题目链接

分析:

但是n*k范围很大,很难搜索。

换思路:从结果出发

搜索树:

//Tip c++ 提交会超时。。。。g++不会
#include <iostream>
#include <cstdio>
#include <queue>using namespace std;//bool visit[MAXN]; 由于状态不会重复,因此不需要采用访问数组去重void BFS(int n){queue<long long> myQueue;myQueue.push(1);while(!myQueue.empty()){long long current=myQueue.front();myQueue.pop();if(current%n==0){printf("%lld\n",current);return;}myQueue.push(current*10);myQueue.push(current*10+1);}
}
int main(){int n;while(scanf("%d",&n)!=EOF){if(n==0){break;}BFS(n);}return 0;
}

小结

宽度优先搜索总结如下

1.状态:需要确定所求解问题中的状态,通过状态的扩展,遍历所有的状态,从中寻找需要的答案
2.状态扩展方式:在宽度优先搜索中,需要尽可能地扩展状态,并对先扩展的状态得到的状态,先进行下一次状态扩展。
3.有效状态。对于有些状态,并不需要再次扩展它,而是直接舍弃它。因为根据问题的分析可知,目标状态不可能由这些状态经过若干次扩展得到,所以直接舍弃。
4.队列。为了使得先得出的状态能够先扩展,于是使用队列,将得到的状态依次放入队尾,每次取对头元素进行扩展
5.标记。为了判断哪些状态是有效的,哪些状态是无效的,往往使用标记
6.有效状态数。问题中的有效状态数与算法的时间复杂度同数量级,所以在进行搜索之前,必须估算其是否在能够接受范围之内
7.最优。宽度优先搜索常常用于求解最优值问题。因为其搜索到的状态总是按照某个关键字递增,这个特性非常适合求解最优值问题。因此,一旦问题出现最少、最短、最优等关键字,就要考虑是否是宽度优先搜索问题。

习题 9.1 玛雅人的密码(清华大学复试上机题)

题目链接

//我的脑残代码,超时了,用了一个set记录已经访问的string,还以为能够减少复杂度,结果反其道而行之超时了。
#include <iostream>
#include <cstdio>
#include <string>
#include <queue>
#include <set>using namespace std;struct passw{string str;int time;passw(){};passw(string s,int n):str(s),time(n){};
};
bool bfs(string str){queue <passw> qs;qs.push(passw(str,0));set <string> ss;while(!qs.empty()){passw ps=qs.front();string s=ps.str;if(ss.find(s)!=ss.end()){continue;//找过了,不用处理了}if(s.find("2012")!=string::npos){//符合printf("%d",ps.time);return true;}else{ss.insert(ps.str);}qs.pop();for(int i=0;i<s.size()-1;i++){string s1=s;char c=s1[i];s1[i]=s1[i+1];s1[i+1]=c;int time=ps.time+1;qs.push(passw(s1,time));}}    return false;
}
int main(){int N;scanf("%d",&N);string str;while(cin>>str){if(bfs(str)){}else{printf("-1\n");}}return 0;
}
//AC代码
#include <iostream>
#include <cstdio>
#include <string>
#include <queue>using namespace std;struct passw{string str;int time;passw(){};passw(string s,int n):str(s),time(n){};
};
bool bfs(string str){queue <passw> qs;qs.push(passw(str,0));while(!qs.empty()){passw ps=qs.front();if(ps.str.find("2012")!=string::npos){//符合printf("%d",ps.time);return true;}qs.pop();for(int i=0;i<ps.str.size()-1;i++){string s1=ps.str;char c=s1[i];s1[i]=s1[i+1];s1[i+1]=c;qs.push(passw(s1,ps.time+1));}}    return false;
}
int main(){int N;scanf("%d",&N);string str;while(cin>>str){if(bfs(str)){}else{printf("-1\n");}}return 0;
}
//大佬代码,采用unorder_map进行优化
#include<iostream>
#include<queue>
#include<string>
#include<unordered_map>using namespace std;struct NewStr{string s;int step;NewStr(int i, string s):step(i), s(s){}
};unordered_map<string, bool> isVisited;//避免重复入队相同字符串
void bfs(string s){queue<NewStr> q;q.push(NewStr(0, s));isVisited[s] = true;while(!q.empty()){NewStr t = q.front();q.pop();string ts = t.s;if(ts.find("2012") != string::npos){cout << t.step << endl;return;}for(int i = 0; i < ts.size() - 1; i ++){swap(ts[i], ts[i + 1]);if(!isVisited[ts]) {q.push(NewStr(t.step + 1, ts));isVisited[ts] = true;}swap(ts[i], ts[i + 1]);}}cout << -1 << endl;
} int main(){int n;string s;while(cin>>n>>s){bfs(s);}return 0;
}

Tip

1.swap()交换,在algorithm文件中,可以用于交换。
2.unordered_map: 链接说明,不会对数据进行排序的map。

9.2 DFS(深度优先遍历)

例题 9.3 A Knight’s Journey

poj - 2488题
题目链接

状态分析
搜索树

选择深度优先搜索策略。

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>using namespace std;int direction[8][2] = {{-1,-2},{1,-2},{-2,-1},{2,-1},{-2,1},{2,1},{-1,2},{1,2}
};const int MAXN=30;bool visit[MAXN][MAXN];bool DFS(int x,int y,int step,string answer,int p,int q){if(step==p*q){cout<<answer<<endl<<endl;return true;}for(int i=0;i<8;i++){int nx=x+direction[i][0];int ny=y+direction[i][1];if(nx<0||nx>=p||ny<0||ny>=q||visit[nx][ny]){continue;//如果坐标不合法,或者访问过了,则不进行处理}visit[nx][ny]=true;char col=ny+'A';char row=nx+'1';if(DFS(nx,ny,step+1,answer+col+row,p,q)){return true;}visit[nx][ny]=false;}return false;
}int main(){int n;scanf("%d",&n);int caseNumber=0;while(n--){int p,q;scanf("%d%d",&p,&q);memset(visit,false,sizeof(visit));cout<<"Scenario #"<<++caseNumber<<":"<<endl;visit[0][0]=true;if(DFS(0,0,1,"A1",p,q)){continue;}else{printf("impossible\n\n");}}return 0;
}

例题 9.4 Square

poj - 2362


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>using namespace std;const int MAXN=25;int stricks[MAXN];
bool visit[MAXN];bool DFS(int sum,int number,int side,int m){if(number==4){return true;}for(int i=0;i<m;i++){if(visit[i]||sum+sticks[i]>side){continue;}visit[i]=true;if(sum+sticks[i]==side){if(DFS(0,number+1,side,m)){return true;}}else{if(DFS(sum+sticks[i],number,side,m)){return true;}}visit[i]=false;}return false;
}int main(){int n;scanf("%d",&n);while(n--){int m;scanf("%d",&m);int length=0;for(int i=0;i<m;i++){scanf("%d",&sticks[i]);length+=sticks[i];}int side=length/4;memeset(visit,false,sizeof(visit));if(DFS(0,0,side,m)){printf("yes\n");}else{printf("no\n");}}
}

但是会超时,考虑减枝



这里拼凑出三个木棍就可以,因为剩下的一根木棍必定可以拼凑出。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>using namespace std;const int MAXN=25;int sticks[MAXN];
bool visit[MAXN];bool DFS(int sum,int number,int position,int side,int m){//剪枝4:position记录上次找的位置if(number==3){//这里找到3根即可返回return true;}int sample=0;for(int i=position;i<m;i++){//剪枝4:从position开始找起if(visit[i]||sum+sticks[i]>side   ||sticks[i]==sample){//剪枝3:如果这根木棍长度与之前那个相同,则不用找了,失败的continue;}visit[i]=true;if(sum+sticks[i]==side){if(DFS(0,number+1,0,side,m)){return true;}else{sample=sticks[i];//剪枝3:找不到,记录长度,后面就不用再找了}}else{if(DFS(sum+sticks[i],number,i+1,side,m)){return true;}else{sample=sticks[i];//剪枝3:同上}}visit[i]=false;}return false;
}bool Compare(int x,int y){return x>y;
}int main(){int n;scanf("%d",&n);while(n--){int m;scanf("%d",&m);int length=0;for(int i=0;i<m;i++){scanf("%d",&sticks[i]);length+=sticks[i];}if(length%4!=0){//剪枝1:如果总长度不能整除4,失败printf("no\n");continue;}int side=length/4;memset(visit,false,sizeof(visit));sort(sticks,sticks+m,Compare);//剪枝2:从大到小排序if(sticks[0]>side){//剪枝2:如果最大的木棍长度超出边长,直接返回失败printf("no\n");continue;}if(DFS(0,0,0,side,m)){printf("yes\n");}else{printf("no\n");}}
}

习题 9.2.1 八皇后

题目链接

#include <iostream>
#include <cstdio>
#include <vector>
#include <string>
#include <algorithm>using namespace std;vector<int> vs;int arr[10];bool Judge(int index,int data){for(int i=1;i<index;i++){if(arr[i]==data||(i-arr[i]==index-data)||(i+arr[i]==index+data)){return false;}}return true;
}
void dfs(int k,int n){//第k行if(k==9){vs.push_back(n);return;}for(int i=1;i<=8;i++){if(Judge(k,i)){arr[k]=i;dfs(k+1,n*10+i);}}
}
int main(){dfs(1,0);sort(vs.begin(),vs.end());int n;while(scanf("%d",&n)!=EOF){printf("%d\n",vs[n-1]);}return 0;
}

第十章 数据结构二

10.1 二叉树


结点

二叉树遍历方式

10.1.1 例10.1 二叉树遍历(清华大学复试上机题)

本题主要考察的知识点是二叉树的建立和二叉树的中序遍历。因此,本题只要递归地建立一棵二叉树即可,再递归的进行中序遍历
此外,注意Build函数中position参数类型是整形引用&。
题目链接

#include <iostream>
#include <cstdio>
#include <string>using namespace std;struct TreeNode{char data;TreeNode *leftChild;TreeNode *rightChild;TreeNode(char c):data(c),leftChild(nullptr),rightChild(nullptr){}
};TreeNode *Build(int &position,string str){char c=str[position++];if(c=='#'){return nullptr;}TreeNode *root=new TreeNode(c);root->leftChild=Build(position,str);root->rightChild=Build(position,str);return root;
}void InOrder(TreeNode *root){if(root==nullptr){return;}InOrder(root->leftChild);printf("%c ",root->data);InOrder(root->rightChild);return;
}int main(){string str;while(cin>>str){int position=0;TreeNode *root=Build(position,str);InOrder(root);printf("\n");}return 0;
}

10.1.2 例10.2 二叉树遍历(华中科技大学复试上机题)

题目链接

前序序列中序序列唯一建立一棵二叉树代码

#include <iostream>
#include <cstdio>
#include <string>using namespace std;struct TreeNode{char data;TreeNode *leftChild,*rightChild;TreeNode(char c):data(c),leftChild(nullptr),rightChild(nullptr){};//建议用nullptr,防止二义性
};TreeNode* Build(string preOrder,string inOrder){if(preOrder.size()==0){return nullptr;}char c=preOrder[0];TreeNode *root=new TreeNode(c);int position=inOrder.find(c);root->leftChild=Build(preOrder.substr(1,position), inOrder.substr(0,position));root->rightChild=Build(preOrder.substr(position+1), inOrder.substr(position+1));return root;
}void PostOrder(TreeNode *root){if(root==nullptr){return ;}PostOrder(root->leftChild);PostOrder(root->rightChild);printf("%c",root->data);
}int main(){string preOrder,inOrder;while(cin>>preOrder>>inOrder){TreeNode *root=Build(preOrder,inOrder);PostOrder(root);printf("\n");}return 0;
}

10.2 二叉排序树

例题10.3 二叉排序树(华中科技大学复试上机题)

题目链接

#include <iostream>
#include <cstdio>using namespace std;struct TreeNode{int data;TreeNode *leftChild;TreeNode *rightChild;TreeNode(int x):data(x),leftChild(nullptr),rightChild(nullptr){}
};TreeNode *Insert(TreeNode *root,int x,int father){if(root==nullptr){root = new TreeNode(x);printf("%d\n",father);}else if(x < root->data){root->leftChild=Insert(root->leftChild,x,root->data);}else if(root->data<x){root->rightChild=Insert(root->rightChild,x,root->data);}return root;
}int main(){int N;while(scanf("%d",&N)!=EOF){TreeNode* root=nullptr;while(N--){int x;scanf("%d",&x);root=Insert(root,x,-1);}}return 0;
}

例题10.4 二叉排序树(华中科技大学复试上机题)

题目链接

#include <iostream>
#include <cstdio>using namespace std;struct TreeNode{int data;TreeNode *leftChild;TreeNode *rightChild;TreeNode(int x):data(x),leftChild(nullptr),rightChild(nullptr){}
};void PreOrder(TreeNode *root){if(root==nullptr){return;}printf("%d ",root->data);PreOrder(root->leftChild);PreOrder(root->rightChild);return;
}void InOrder(TreeNode *root){if(root==nullptr){return;}InOrder(root->leftChild);printf("%d ",root->data);InOrder(root->rightChild);return;
}void PostOrder(TreeNode *root){if(root==nullptr){return;}PostOrder(root->leftChild);PostOrder(root->rightChild);printf("%d ",root->data);return;
}TreeNode *Insert(TreeNode *root,int x){if(root==nullptr){root = new TreeNode(x);}else if(x < root->data){root->leftChild=Insert(root->leftChild,x);}else if(root->data<x){root->rightChild=Insert(root->rightChild,x);}return root;
}int main(){int n;while(scanf("%d",&n)!=EOF){TreeNode* root=nullptr;while(n--){int x;scanf("%d",&x);root=Insert(root,x);}PreOrder(root);printf("\n");InOrder(root);printf("\n");PostOrder(root);printf("\n");}return 0;
}

习题 10.1 二叉搜索树

题目链接

递归建立二叉搜索树代码记忆!!

#include <iostream>
#include <cstdio>using namespace std;
struct TreeNode{int data;TreeNode *leftChild,*rightChild;TreeNode(int n):data(n),leftChild(nullptr),rightChild(nullptr){};
};TreeNode *Insert(TreeNode *root,int n){if(root==nullptr){root=new TreeNode(n);}else if(root->data<n){root->rightChild=Insert(root->rightChild,n);}else if(root->data>n){root->leftChild=Insert(root->leftChild,n);}return root;
}bool same(TreeNode *root1,TreeNode *root2){if(root1==nullptr&&root2==nullptr){return true;}if(root1->data==root2->data&&same(root1->leftChild,root2->leftChild)&&same(root1->rightChild,root2->rightChild)){return true;}return false;
}
int main(){string str;int n;while(scanf("%d",&n)!=EOF){if(n==0){break;}string str;cin>>str;TreeNode *root=nullptr;for(int i=0;i<str.length();i++){root=Insert(root,str[i]-'0');}while(n--){cin>>str;TreeNode *root1=nullptr;for(int i=0;i<str.length();i++){root1=Insert(root1,str[i]-'0');}if(same(root,root1)){printf("YES\n");}else{printf("NO\n");}}}return 0;
}

10.3 优先队列

优先队列实现原理

复杂度:查询O(1) ,入队O(logn),出队O(logn)

priority_queue

priority_quque使用

 #include <cstdio>
#include <queue>using namespace std;priority_queue<int> myPriorityQueue;int main(){int arr[10]={3,2,4,1,5,6,9,0,8,7};for(int i=0;i<10;i++){myPriorityQueue.push(arr[i]);}int sum=0;while(!myPriorityQueue.empty()){printf("%d ",myPriorityQueue.top());//注意这里是top()。队列是frontsum+=myPriorityQueue.top();myPriorityQueue.pop();}
}

优先队列作用

例题10.5 复数集合

题目链接

#include <cstdio>
#include <queue>
#include <iostream>
#include <string>using namespace std;struct Complex{int real;int imag;Complex(int a,int b):real(a),imag(b){}bool operator < (Complex c) const{if(real *real + imag*imag == c.real * c.real + c.imag * c.imag){//注意这里实现 比较,比较模长可以通过平方比较return imag > c.imag;}return real *real + imag*imag < c.real * c.real + c.imag * c.imag;}
};int main(){int n;while(scanf("%d",&n)!=EOF){priority_queue<Complex> myPriorityQueue;while(n--){string str;cin>>str;if(str=="Pop"){if(myPriorityQueue.empty()){printf("empty\n");}else{Complex current=myPriorityQueue.top();//注意这里是topmyPriorityQueue.pop();printf("%d+i%d\n",current.real,current.imag);printf("SIZE = %d\n",myPriorityQueue.size());}}else{int a,b;scanf("%d+i%d",&a,&b);myPriorityQueue.push(Complex(a,b));printf("SIZE = %d\n",myPriorityQueue.size());}}}return 0;
}

例题10.6 哈夫曼树

#include <iostream>
#include <cstdio>
#include <queue>using namespace std;int main(){int n;while(scanf("%d",&n)!=EOF){priority_queue<int,vector<int>,greater<int> > myPriorityQueue;while(n--){int x;scanf("%d",&x);myPriorityQueue.push(x);}int answer=0;while(myPriorityQueue.size()>1){int a=myPriorityQueue.top();myPriorityQueue.pop();int b=myPriorityQueue.top();myPriorityQueue.pop();answer+=(a+b);myPriorityQueue.push(a+b);}printf("%d",answer);}return 0;
}

习题 10.3 查找第K小的数(北京邮电大学复试上机题)

题目链接

TIP

1.注意priority_queue默认为大顶堆,如果想要创建小顶堆,可以

priority_queue <int,vector,greater > pq; 注意两个 > >中间要写一个空格。

#include <iostream>
#include <queue>
#include <algorithm>using namespace std;int main(){int n;scanf("%d",&n);priority_queue <int,vector<int>,greater<int> > pq;for(int i=0;i<n;i++){int num;scanf("%d",&num);pq.push(num);}int k;scanf("%d",&k);int cnt;int previous;if(k==0){printf("%d",pq.top());return 0;}while(k--){if(previous==pq.top()){k++;}previous=pq.top();pq.pop();}printf("%d",previous);return 0;
}

习题 10.3 搬水果(吉林大学复试上机题)

题目链接

#include <iostream>
#include <queue>
#include <cstdio>using namespace std;int main(){int n;while(scanf("%d",&n)!=EOF){if(n==0){break;}priority_queue <int,vector<int>,greater<int> > q;int answer=0;for(int i=0;i<n;i++){int weight;scanf("%d",&weight);q.push(weight);}while(q.size()>=2){int one=q.top();q.pop();int two=q.top();q.pop();answer+=(one+two);q.push(one+two);}printf("%d\n",answer);}return 0;
}

10.4 散列表(map,unordered_map)

散列表思想

C++提供的映射及时间复杂度

map容器
使用例子:

#include <cstdio>
#include <iostream>
#include <map>using namespace std;map <string,int> myMap;//内部按照关键字进行排序,底层为红黑树int main(){//插入,添加元素myMap["Emma"]=67;myMap.insert(pair<string,int>("Bob",72));//查cout<<myMap["Emma"]<<endl;cout<<myMap.at("Bob")<<endl;//删除myMap.erase("Emma");map<string,int>::iterator it;for(it=myMap.begin();it!=myMap.end();it++){cout << it->first <<" "<<it->second<<endl;}//查找it=myMap.find("Bob");//返回迭代器if(it!=myMap.end()){return 0;//未找到}cout<<myMap.size();myMap.clear();//晴空}

10.4.1 例题10.7 查找学生信息

#include <cstdio>
#include <iostream>
#include <map>using namespace std;map <string,string> student;int main(){int n;scanf("%d",&n);getchar();//这里吃掉回车符while(n--){string str;getline(cin,str);int position=str.find(" ");string key=str.substr(0,position);student[key]=str;}int m;scanf("%d",&m);while(m--){string key;cin>>key;map<string,string>::iterator it=student.find(key);if(it!=student.end()){cout<<it->second<<endl;}else{cout<<"No Answer!"<<endl;}
或者可以采用下面的方式判断
//         string answer=student[key];
//         if(answer==""){//             answer="No Answer";
//         }
//         cout<<answer<<endl;}return 0;
}

10.4.2 例题10.8 魔咒词典(浙江大学复试上机题)

题目链接

#include <iostream>
#include <cstdio>
#include <string>
#include <map>using namespace std;map<string,string> dictionary;int main(){string str;while(getline(cin,str)){if(str=="@END@"){break;}int pos=str.find("]");string key=str.substr(0,pos+1);string value=str.substr(pos+2);dictionary[key]=value;dictionary[value]=key;}int n;scanf("%d",&n);getchar();while(n--){string key;getline(cin,key);string answer=dictionary[key];if(answer==""){answer="what?";}else if(answer[0]=='['){answer=answer.substr(1,answer.size()-2);}cout<<answer<<endl;}return 0;
}

10.4.3 例题10.8 子串计算(北京大学复试上机题)

题目链接

迭代器it,访问元素,it->first,it->second

#include <iostream>
#include <cstdio>
#include <string>
#include <map>using namespace std;int main(){string str;while(cin>>str){map<string,int> number;for(int i=0;i<=str.size();i++){//i表示子串结束位置下标for(int j=0;j<i;j++){//j表示子串起始位置string key=str.substr(j,i-j);//每个子串number[key]++;//映射值加一}}map<string,int>::iterator it;//定义迭代器for(it=number.begin();it!=number.end();it++){if(1<it->second){cout<<it->first<<" "<<it->second<<endl;}}}return 0;
}

10.4.4 习题 10.4 统计同成绩学生人数(浙江大学复试上机题)

题目链接

#include <iostream>
#include <cstdio>
#include <map>using namespace std;int main(){int N;while(scanf("%d",&N)!=EOF){if(N==0){break;}map<int,int> mp;int score;for(int i=0;i<N;i++){scanf("%d",&score);mp[score]++;}scanf("%d",&score);printf("%d\n",mp[score]);}return 0;
}

10.4.5 习题 10.5 开门人和关门人(浙江大学复试上机题)

题目链接

//用排序写的
#include <iostream>
#include <cstdio>
#include <queue>
#include <string>
#include <algorithm>using namespace std;struct info{string num;string time1;string time2;
};
const int maxn=10000+5;
info arr[maxn];
bool cmp1(info info1,info info2){return info1.time1<info2.time1;
}
bool cmp2(info info1,info info2){return info1.time2>info2.time2;
}
int main(){int M;scanf("%d",&M);for(int i=0;i<M;i++){cin>>arr[i].num>>arr[i].time1>>arr[i].time2;}sort(arr,arr+M,cmp1);cout<<arr[0].num<<" ";sort(arr,arr+M,cmp2);cout<<arr[0].num;return 0;
}

10.4.6 习题 10.6 谁是你的潜在朋友(北京大学复试上机题)

题目链接

#include <iostream>
#include <cstdio>
#include <map>using namespace std;const int  MAXN=10000+5;
int arr[MAXN];
int main(){int N,M;scanf("%d %d",&N,&M);map<int,int>booklike;for(int i=0;i<N;i++){scanf("%d",&arr[i]);booklike[arr[i]]++;}for(int i=0;i<N;i++){if(booklike[arr[i]]==1){printf("BeiJu\n");}else{printf("%d\n",booklike[arr[i]]-1);}}return 0;
}

第十一章 图论问题

体系:

11.1 概述

定义

分类



存储方式




使用例子

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;const int MAXN=1000;int graph[MAXN][MAXN];//邻接矩阵存储图vector<int> graph1[MAXN];//邻接表表示图,向量表示顶点表

11.2 并查集

集合概念

并查集表示

并查集操作

查询Find,返回根节点即可,判断根结点是否相等就可以判断是否属于同一个集合

合并:将一棵树作为另外一棵树的子树即可

并查集简单实现

#include <iostream>
#include <cstdio>using namespace std;const int MAXN=1000;int father[MAXN];void Initial(int n){for(int i=0;i<n;i++){father[i]=i;//初始化每个结点只有自己}return;
}int Find(int x){if(x!=father[x]){return Find(father[x]);}return father[x];
}void Union(int x,int y){x=Find(x);y=Find(x);if(x!=y){father[y]=x;}
}int main(){return 0;
}

但是并查集查询效率取决与树高,而上述代码未进行优化,因此要进行路径压缩,矮树合并到高树。

优化并查集

#include <iostream>
#include <cstdio>using namespace std;const int MAXN=1000;int father[MAXN];
int height[MAXN];//用于矮树合并到高树上void Initial(int n){for(int i=0;i<n;i++){father[i]=i;//初始化每个结点只有自己height[i]=0;}return;
}int Find(int x){if(x!=father[x]){father[x]=Find(father[x]);//查找过程中将所有子孙节点直接挂接到根上}return father[x];
}void Union(int x,int y){x=Find(x);y=Find(y);if(x!=y){if(height[x]<height[y]){//矮树合并到高树上father[x]=y;}else if(height[x]>height[y]){father[y]=x;}else{father[y]=x;height[x]++;//注意这里更新树高}}
}int main(){return 0;
}

11.2.1 例题11.2 连通图

题目链接在
说明


#include <iostream>
#include <cstdio>using namespace std;const int MAXN=1000+50;int father[MAXN];
int height[MAXN];//用于矮树合并到高树上void Initial(int n){for(int i=0;i<=n;i++){father[i]=i;//初始化每个结点只有自己height[i]=0;}return;
}int Find(int x){if(x!=father[x]){father[x]=Find(father[x]);//查找过程中将所有子孙节点直接挂接到根上}return father[x];
}void Union(int x,int y){x=Find(x);y=Find(y);if(x!=y){if(height[x]<height[y]){//矮树合并到高树上father[x]=y;}else if(height[x]>height[y]){father[y]=x;}else{father[y]=x;height[x]++;//注意这里更新树高}}
}int main(){int n,m;while(scanf("%d%d",&n,&m)!=EOF){if(n==0&&m==0){break;}Initial(n);//初始化!!while(m--){int x,y;scanf("%d%d",&x,&y);Union(x,y);}int component=0;for(int i=1;i<=n;i++){if(i==Find(i)){component++;}}if(component==1){printf("YES\n");}else{printf("NO\n");}}return 0;
}

11.2.2 例题11.1 畅通工程(浙江大学上机题)

Tip!!!
注意并查集的FInd函数的书写,尤其是递归操作!!!!!!
对于每一个输入案例,别忘了Initial函数!!!!

#include <iostream>
#include <cstdio>using namespace std;const int MAXN=1000+5;int father[MAXN];
int height[MAXN];void Initial(){for(int i=0;i<MAXN;i++){father[i]=i;height[i]=1;}return;
}
int Find(int x){//注意这里代码书写,容易写错!!if(x!=father[x]){father[x]=Find(father[x]);}return father[x];
}
void Union(int x,int y){x=Find(x);y=Find(y);if(x==y){return;}if(height[x]<height[y]){father[y]=x;}else if(height[y]>height[x]){father[x]=y;}else{father[x]=y;height[y]++;}return;
}int main(){int N,M;while(scanf("%d",&N)!=EOF){if(N==0){break;}Initial();//注意这里每次测试用例都要Initial!!!!!scanf("%d",&M);for(int i=0;i<M;i++){int x,y;scanf("%d%d",&x,&y);Union(x,y);}int answer=-1;for(int i=1;i<=N;i++){if(i==Find(i)){answer++;}}printf("%d\n",answer);}return 0;
}

11.2.3 例题11.3 Is It A Tree(北京大学上机题)

题目链接

#include <iostream>
#include <cstdio>using namespace std;const int MAXN=10000;int father[MAXN];//父亲结点
int height[MAXN];//结点高度
int inDegree[MAXN];//入度
bool visit[MAXN];//标记void Initial(){//初始化for(int i=0;i<MAXN;i++){father[i]=i;height[i]=0;inDegree[i]=0;visit[i]=false;}
}int Find(int x){if(x!=father[x]){father[x]=Find(father[x]);}return father[x];
}void Union(int x,int y){x=Find(x);y=Find(y);if(x!=y){if(height[x]<height[y]){father[x]=y;}else if(height[y]<height[x]){father[y]=x;}else{father[y]=x;height[x]++;}}return;
}bool isTree(){bool flag=true;int component=0;//连通分量数目int root=0;//根结点数目for(int i=0;i<MAXN;i++){if(!visit[i]){continue;}if(father[i]==i){component++;}if(inDegree[i]==0){root++;}else if(inDegree[i]>1){//入度不满足要求flag=false;        }}if(component!=1||root!=1){//不符合树的定义flag=false;}if(component==0&&root==0){//空集也是树flag=true;}return flag;
}
int main(){int x,y;int caseNumber=0;Initial();while(scanf("%d%d",&x,&y)!=EOF){if(x==-1&&y==-1){break;}if(x==0&&y==0){if(isTree()){printf("Case %d is a tree.\n",++caseNumber);}else{printf("Case %d is not a tree.\n",++caseNumber);}Initial();}else{Union(x,y);inDegree[y]++;visit[x]=true;visit[y]=true;}}return 0;
}

11.2.4 习题11.1 找出直系亲属(浙江大学复试上机题)

题目链接

//大佬代码
#include <iostream>
#include <string>
using namespace std;
const int MAXN = 50;int son[MAXN];      //父节点是儿子,所以以son命名
int height[MAXN];    //表示结点高度,最小的儿子高度为0,越往下辈分越大//并查集思想void Initial(){                //初始化函数  父节点默认设为自己,高度默认为0for(int i=0;i<MAXN;i++){son[i] = i;height[i] = 0;}
}int Find(int x,int y,int count){   if(son[x]==son[y] && x!=y && son[x]!=x && son[y]!=y) return -1;  //若当前两个节点的父节点是同一个,即拥有同个儿子,则不是直系if(height[x]<height[y]){  //高度高的辈分大,则辈分高取自己的儿子,然后递归find,count作为记录取儿子的次数,取一次表面差一个辈分y = son[y];count++;return Find(x,y,count);}else if(height[x]>height[y]){  //同理x = son[x];count++;return Find(x,y,count);}else{return count;   }return -1;
}int main(){int n,m;while(cin >> n >> m){Initial();for(int i=0;i<n;i++){string str;cin >> str;int a = str[0] - 'A';    //由于是A-Z的字符,则用-'A'来变成数,方便记录son数组的值if(str[1]!='-'){  //如果缺失则跳过int b = str[1] - 'A';son[b] = a;       //记录自己的儿子节点height[b] = 1 + height[a];  //b的高度是儿子的高度加1,以此类推,高度越高辈分越大}if(str[2]!='-'){  int c = str[2] - 'A';son[c] = a;height[c] = 1 + height[a];}}for(int i=0;i<m;i++){string str;cin >> str;int a = str[0]-'A';int b = str[1]-'A';//两个判断有点冗余,可以封装成函数作为调用if(height[a]>=height[b]){   //若a高度大于b,则a的辈分高,输出parentint ans = Find(a,b,0);string str1 = "";if(ans==-1){str1 += "-";cout << str1 << endl;}else if(ans==1){str1 += "parent";cout << str1 << endl;}else if(ans==2){str1 += "grandparent";cout << str1 << endl;}else if(ans>2){str1 += "grandparent";while(ans>2){str1 = "great-" + str1;ans--;}cout << str1 << endl;}}else if(height[a]<height[b]){   //若a的高度低,则a的辈分低,输出childint ans = Find(a,b,0);string str1 = "";if(ans==-1){str1 += "-";cout << str1 << endl;}else if(ans==1){str1 += "child";cout << str1 << endl;}else if(ans==2){str1 += "grandchild";cout << str1 << endl;}else if(ans>2){str1 += "grandchild";while(ans>2){str1 = "great-" + str1;ans--;}cout << str1 << endl;}}}   }return 0;
}

11.2.5 习题11.2 第一题(上海交通大学复试上机题)

题目链接

注意本题采用map结构构造并查集,或者开一个大数组

#include <iostream>
#include <cstdio>
#include <map>using namespace std;//用map实现并查集!!
map<int,int> father;
map<int,int> height;
int Find(int x){if(father.find(x)!=father.end()){if(father[x]!=x){//只有比较里面用x,其他都是father[x]father[x]=Find(father[x]);//!!!!!!!!!!!注意这里是Find(father[x])!!!!!!!!}}else{father[x]=x;height[x]=1;}return father[x];
}
void Union(int x,int y){x=Find(x);y=Find(y);if(x==y){return;}if(height[x]>height[y]){father[y]=x;}else if(height[x]<height[x]){father[x]=y;}else{father[x]=y;height[y]++;}
}int main(){int i,j;while(scanf("%d%d",&i,&j)!=EOF){Union(i,j);}int answer=0;for(auto it=father.begin();it!=father.end();it++){if(it->first==it->second){answer++;}}printf("%d\n",answer);return 0;
}

11.2.6 习题11.3 Head of a Gang(浙江大学复试上机题)

太菜了我,大神代码

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
using namespace std;
#define MAX_N 2000
int parent[MAX_N], r[MAX_N], k[MAX_N], w[MAX_N];
int population = 0;
map<string, int> indexs;
string names[MAX_N * 2];
vector<int> gangs[MAX_N];
int head[MAX_N], cnt = 0;
void init(int N){for(int i = 0; i < MAX_N; i++){parent[i] = i;w[i] = 0;r[i] = 0;k[i] = 0;head[i]=0;gangs[i].clear();}population=0;indexs.clear();cnt=0;}
int find(int p){while (p != parent[p]) {parent[p] = parent[parent[p]]; // 路径压缩p = parent[p];}return p;
}
bool connected(int p, int q){return find(p) == find(q);
}
// union是保留关键字,所以函数名不能为union
void Union(int p, int q, int t){int rootP = find(p), rootQ = find(q);if(rootP == rootQ){k[rootP] += t;return;}// 总是将小树合并到大树上,如果两棵树高度一样,则任意合并,合并后树的高度加1,合并树的同时合并计数if(r[rootP] < r[rootQ]){parent[rootP] = rootQ;k[rootQ] += k[rootP] + t; // 合并并累加k}else if(r[rootP] > r[rootQ]){parent[rootQ] = rootP;k[rootP] += k[rootQ] + t; // 合并并累加k}else{parent[rootQ] = rootP;k[rootP] += k[rootQ] + t; // 合并并累加kr[rootP]++;}
}// 获得下标
int getIndex(string s){if(indexs.find(s) == indexs.end()){indexs[s] = population;names[population] = s;return population++;}return indexs[s];
}
bool cmp(int h1, int h2){return names[h1] < names[h2];
}int main() {int N, K;// 读取输入while(scanf("%d %d", &N, &K)!=EOF){init(N);string s1, s2;for(int i = 0, u, v, t; i < N; i++){cin >> s1 >> s2;scanf("%d", &t);u = getIndex(s1);v = getIndex(s2);Union(u, v, t);w[u] += t;w[v] += t;}// 将每个人加入到自己的组中for(int i = 0; i < population; i++){// 这里不能直接使用parent[i],因为可能存在需要路径压缩gangs[find(i)].push_back(i);}for(int i = 0, size, h; i < population; i++){// 过滤k值和人数不符合的组if(k[i] <= K || (size = (int)gangs[i].size()) <= 2) continue;// 遍历组内成员获得头目h = i;for(int p : gangs[i]){if(w[p] > w[h]){h = p;}}head[cnt++] = h;}// 对头目按字典名排序sort(head, head + cnt, cmp);// 输出结果printf("%d\n", cnt);for(int i = 0, p; i < cnt; i++){p = head[i];// 但是这里又可以直接使用parent[p],因为在前面加入组的时候对每个点都执行了一次find(p),全部被路径压缩printf("%s %d\n", names[head[i]].c_str(), (int)gangs[parent[p]].size());}}return 0;
}

11.3 最小生成树

算法实现

Kruskal 时间复杂度O(ElogE)

时间复杂度分析

一般采用Kruskal算法即可。

11.3.1 例题11.4 还是畅通工程

题目链接

#include <iostream>
#include <cstdio>
#include <algorithm>using namespace std;const int MAXN=100;struct Edge{int from;int to;int length;bool operator <(const Edge& e) const{return length<e.length;}
};Edge edge[MAXN*MAXN];
int father[MAXN];
int height[MAXN];//用于矮树合并到高树上void Initial(int n){for(int i=0;i<=n;i++){father[i]=i;//初始化每个结点只有自己height[i]=0;}return;
}int Find(int x){if(x!=father[x]){father[x]=Find(father[x]);//查找过程中将所有子孙节点直接挂接到根上}return father[x];
}void Union(int x,int y){x=Find(x);y=Find(y);if(x!=y){if(height[x]<height[y]){//矮树合并到高树上father[x]=y;}else if(height[x]>height[y]){father[y]=x;}else{father[y]=x;height[x]++;//注意这里更新树高}}
}int Kruskal(int n,int edgeNumber){Initial(n);sort(edge,edge+edgeNumber);int sum=0;for(int i=0;i<edgeNumber;i++){Edge current=edge[i];if(Find(current.from)!=Find(current.to)){Union(current.from,current.to);sum+=current.length;}}return sum;
}int main(){int n;while(scanf("%d",&n)!=EOF){if(n==0){break;}int edgeNumber=n*(n-1)/2;for(int i=0;i<edgeNumber;i++){scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].length);}int answer=Kruskal(n,edgeNumber);printf("%d\n",answer);}return 0;
}

11.3.3 例题11.5 继续畅通工程

题目链接

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#include <climits>
#include <algorithm>using namespace std;const int MAXN=100;struct Edge{int from;int to;int length;bool operator<(const Edge&e)const{return length<e.length;}
};Edge edge[MAXN*MAXN];
int father[MAXN];
int height[MAXN];void Initial(int n){for(int i=0;i<=n;i++){father[i]=i;height[i]=0;}
}
int Find(int x){if(x!=father[x]){father[x]=Find(father[x]);}return father[x];
}
void Union(int x,int y){x=Find(x);y=Find(y);if(x!=y){if(height[x]<height[y]){father[x]=y;}else if(height[x]>height[y]){father[y]=x;}else{father[y]=x;height[x]++;}}return;
}
int Kruskal(int n,int edgeNumber){Initial(n);sort(edge,edge+edgeNumber);int sum=0;for(int i=0;i<edgeNumber;i++){Edge current=edge[i];if(Find(current.from)!=Find(current.to)){Union(current.from,current.to);sum+=current.length;}}return sum;
}
int main(){int n;while(scanf("%d",&n)!=EOF){if(n==0){break;}int edgeNumber=n*(n-1)/2;for(int i=0;i<edgeNumber;i++){int status;scanf("%d%d%d%d",&edge[i].from,&edge[i].to,&edge[i].length,&status);if(status){edge[i].length=0;}}int answer=Kruskal(n, edgeNumber);printf("%d\n",answer);}return 0;
}

11.3.4 习题11.4 Freckles

Tip:
注意类型书写成double,否则会出现结果为0的错误

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>using namespace std;const int MAXN=100;
struct Point{double x;double y;Point(int a,int b):x(a),y(b){};Point(){};
};
struct Edge{int from;int to;double length;bool operator <(const Edge& e) const{return length<e.length;}Edge(int f,int t,double l):from(f),to(t),length(l){};Edge(){};
};Point points[MAXN];
Edge edge[MAXN*MAXN];
int father[MAXN];
int height[MAXN];//用于矮树合并到高树上void Initial(int n){for(int i=0;i<=n;i++){father[i]=i;//初始化每个结点只有自己height[i]=0;}return;
}int Find(int x){if(x!=father[x]){father[x]=Find(father[x]);//查找过程中将所有子孙节点直接挂接到根上}return father[x];
}void Union(int x,int y){x=Find(x);y=Find(y);if(x!=y){if(height[x]<height[y]){//矮树合并到高树上father[x]=y;}else if(height[x]>height[y]){father[y]=x;}else{father[y]=x;height[x]++;//注意这里更新树高}}
}double Kruskal(int n,int edgeNumber){Initial(n);sort(edge,edge+edgeNumber);double sum=0;for(int i=0;i<edgeNumber;i++){Edge current=edge[i];if(Find(current.from)!=Find(current.to)){Union(current.from,current.to);sum+=current.length;}}return sum;
}int main(){int n;while(scanf("%d",&n)!=EOF){int num=0;if(n==0){break;}double x,y;for(int i=0;i<n;i++){scanf("%lf %lf",&x,&y);points[i]=Point(x,y);}int cnt=0;for(int i=0;i<n;i++){for(int j=i+1;j<n;j++){Point p1=points[i];Point p2=points[j];double dis=sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));edge[cnt++]=Edge(i,j,dis);}}printf("%.2f",Kruskal(n, cnt));}return 0;
}

11.4 最短路径

Dijkstra Algorithm

11.4.1 畅通工程续

杭电oj-1874题

注意本题邻接表的写法,以及获取元素方法。
Dijstra Algorithm写法

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#include <climits>using namespace std;const int MAXN=200;
const int INF=INT_MAX;//无穷大或者0x7FFFFFFFstruct Edge{int to;int length;Edge(int t,int l):to(t),length(l){}
};vector <Edge> graph[MAXN];
int dis[MAXN];
bool visit[MAXN];void Dijkstra(int start,int n){memset(visit,false,sizeof(visit));fill(dis,dis+n,INF);dis[start]=0;for(int i=0;i<n;i++){int u=-1;for(int j=0;j<n;j++){if(visit[j]){//已经查找过continue;}if(u==-1||dis[j]<dis[u]){u=j;//更新最小值下标索引}}for(int j=0;j<graph[u].size();j++){int v=graph[u][j].to;int d=graph[u][j].length;if(dis[u]+d<dis[v]){dis[v]=dis[u]+d;}}visit[u]=true;}return;
}int main(){int n,m;while(scanf("%d%d",&n,&m)!=EOF){memset(graph,0,sizeof(graph));while(m--){int from,to,length;scanf("%d%d%d",&from,&to,&length);graph[from].push_back(Edge(to,length));//注意这里是无向图,对于每一条边邻接表中有两个边表结点需要添加graph[to].push_back(Edge(from,length));}int start;int terminal;scanf("%d%d",&start,&terminal);Dijkstra(start,n);if(dis[terminal]==INF){dis[terminal]=-1;}}return 0;
}

针对于Dijstra算法的优化
采用优先队列选择离原点最近的点

//优化算法如下
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#include <climits>using namespace std;const int MAXN=200;
const int INF=INT_MAX;//无穷大或者0x7FFFFFFFstruct Edge{int to;int length;Edge(int t,int l):to(t),length(l){}
};struct Point{int number;int distance;Point(int n,int d):number(n),distance(d){}bool operator < (const Point&p) const{return distance > p.distance;}
};vector <Edge> graph[MAXN];
int dis[MAXN];
bool visit[MAXN];void Dijkstra(int start,int n){fill(dis,dis+n,INF);dis[start]=0;//这里进行算法优化,采用优先队列选择下一个点priority_queue<Point> myPriorityQueue;myPriorityQueue.push(Point(start,dis[start]));while(!myPriorityQueue.empty()){int u=myPriorityQueue.top().number;myPriorityQueue.pop();for(int j=0;j<graph[u].size();j++){int v=graph[u][j].to;int d=graph[u][j].length;if(dis[u]+d<dis[v]){dis[v]=dis[u]+d;myPriorityQueue.push(Point(v,dis[v]));}}}return;
}int main(){int n,m;while(scanf("%d%d",&n,&m)!=EOF){memset(graph,0,sizeof(graph));while(m--){int from,to,length;scanf("%d%d%d",&from,&to,&length);graph[from].push_back(Edge(to,length));//注意这里是无向图,对于每一条边邻接表中有两个边表结点需要添加graph[to].push_back(Edge(from,length));}int start;int terminal;scanf("%d%d",&start,&terminal);Dijkstra(start,n);if(dis[terminal]==INF){dis[terminal]=-1;}}return 0;
}

11.4.2 例题11.7 最短路径问题

题目链接

本题只需添加代价数组,然后在更新距离过程中考虑距离相同时代价也要比较更新即可。

if((dis[v]==dis[u]+l&&cost[v]>cost[u]+p)||dis[v]>dis[u]+l){//注意松弛操作的条件变换dis[v]=dis[u]+l;//注意这里是l,不是1cost[v]=cost[u]+p;myPriorityQueue.push(Point(v,dis[v]));}
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#include <climits>using namespace std;const int MAXN=1000+5;
const int INF=INT_MAX;//无穷设为很大的数struct Edge{int to;//终点int length;//长度int price;//花费Edge(int t,int l,int p):to(t),length(l),price(p){}
};
struct Point{int number;//点的编号int distance;//原点到该点的距离Point(int n,int d):number(n),distance(d){}bool operator < (const Point&p)const{return distance>p.distance;//距离小的优先级高}
};
vector<Edge> graph[MAXN];//邻接表实现图
int dis[MAXN];//原点到各点距离
int cost[MAXN];//记录花费
void Dijstra(int s){priority_queue <Point> myPriorityQueue;dis[s]=0;cost[s]=0;myPriorityQueue.push(Point(s,dis[s]));while(!myPriorityQueue.empty()){int u=myPriorityQueue.top().number;//离原点最近的点myPriorityQueue.pop();for(int i=0;i<graph[u].size();i++){int v=graph[u][i].to;int l=graph[u][i].length;int p=graph[u][i].price;if((dis[v]==dis[u]+l&&cost[v]>cost[u]+p)||dis[v]>dis[u]+l){//注意松弛操作的条件变换dis[v]=dis[u]+l;//注意这里是l,不是1cost[v]=cost[u]+p;myPriorityQueue.push(Point(v,dis[v]));}}}return;
}
int main(){int n,m;while(scanf("%d%d",&n,&m)!=EOF){if(n==0&&m==0){break;}memset(graph,0,sizeof(graph));//图初始化fill(dis,dis+n+1,INF);//距离初始化为无穷fill(cost,cost+n+1,INF);//花费初始化为无穷while(m--){int from,to,length,price;scanf("%d%d%d%d",&from,&to,&length,&price);graph[from].push_back(Edge(to,length,price));graph[to].push_back(Edge(from,length,price));}int s,t;scanf("%d%d",&s,&t);Dijstra(s);printf("%d %d\n",dis[t],cost[t]);}return 0;
}

11.4.3 习题11.6 最短路径(上海交通大学复试上机题)

题目链接

本题乍一看是单元最短路径问题,这是一道披着最短路径外衣的最小生成树,因为路径长度是从20开始,其实举例到第3条路径就可以发现:22=4>20+21,也就是说,当一条边的两个端点不在同一集合时,这条边的长度就是这两点间的最短距离,可以直接取mod,这时只用更新一下两个集合里各点间的距离就行,而如果这条边的两个端点在同一集合,那么这条边就可以直接舍去了,因为这条边的长度将会比之前出现的所有边长度的总和还要长。
因此,只需要采用最小生成树的思想,对最小生成树的各条边进行Dijstra算法即可。

/*方法二,使用并查集,因为后面边的长度比前面所有边长之和还要长*/
/*所以后面加的边就不用再考虑,如果两个点不属于一个集合,*/
/*直接合并这两个点即可,此时长度一定是最短的*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <climits>
#include <queue>using namespace std;const int MAXN=100+20;
const int MOD=100000;
const int INF=INT_MAX;
int father[MAXN];
int height[MAXN];void Initial(){for(int i=0;i<MAXN;i++){father[i]=i;height[i]=1;}return;
}
int Find(int x){if(father[x]!=x){father[x]=Find(father[x]);}return father[x];
}
void Union(int x,int y){x=Find(x);y=Find(y);if(x==y){return;}if(height[x]<height[y]){father[x]=y;}else if(height[x]>height[x]){father[y]=x;}else{father[x]=y;height[y]++;}return;
}
struct Point{int number=0;int distance;Point(int n,int d):number(n),distance(d){}bool operator<(const Point&p)const{return distance>p.distance;}
};
struct Edge{int to;int k;//第几条路Edge(int t,int kt):to(t),k(kt){}Edge(){}bool operator < (Edge e) const{return k<e.k;}
};
vector<Edge> edges[MAXN];
int dis[MAXN];
void Dijstra(int start,int n){fill(dis,dis+n+1,INF);dis[start]=0;priority_queue<Point> Q;Q.push(Point(start,0));while(!Q.empty()){Point p=Q.top();Q.pop();int from=p.number;for(int i=0;i<edges[from].size();i++){int to=edges[from][i].to;int len=edges[from][i].k;if(dis[to]>dis[from]+len){dis[to]=dis[from]+len;Q.push(Point(to,dis[to]));}}}return;
}
int main(){int N,M;scanf("%d%d",&N,&M);int c1,c2;Initial();int len=1;for(int i=0;i<M;i++){scanf("%d%d",&c1,&c2);if(Find(c1)!=Find(c2)){Union(c1,c2);edges[c1].push_back(Edge(c2,len));edges[c2].push_back(Edge(c1,len));}len=(len*2)%MOD;}Dijstra(0,N);for(int i=1;i<N;i++){printf("%d\n",(dis[i]==INF)?-1:dis[i]%MOD);}return 0;
}

11.4.4 习题11.7 I Wanna Go Homw(北京大学复试上机题)

分析:跟前面题思路类似,在松弛算法上添加约束,经过分析可以知道路程中只能移动一次从1->2,不能有2->1的情况,因此添加这层约束即可

#include <iostream>
#include <queue>
#include <cstdio>
#include <climits>using namespace std;const int MAXN=600+10;
const int INF=INT_MAX;
struct Point{int number;int distance;Point(int n,int d):number(n),distance(d){};Point(){};bool operator<(Point p)const{return distance>p.distance;}
};
struct Edge{int to;int length;Edge(int t,int l):to(t),length(l){};Edge(){};
};
vector <Edge>edges[MAXN];
int support[MAXN];
int dis[MAXN];
void Dijstra(int start){dis[start]=0;priority_queue<Point> Q;Q.push(Point(start,dis[start]));while(!Q.empty()){int from=Q.top().number;Q.pop();for(int i=0;i<edges[from].size();i++){int to=edges[from][i].to;int length=edges[from][i].length;if(dis[to]>dis[from]+length&&!(support[from]==2&&support[to]==1)){//这里添加约束,不能从2-1dis[to]=dis[from]+length;Q.push(Point(to,dis[to]));}}}return;
}
void Initial(){for(int i=1;i<=MAXN;i++){edges[i].clear();support[i]=0;dis[i]=INF;}
}
int main(){int N;while(scanf("%d",&N)!=EOF){if(N==0){break;}Initial();int M;scanf("%d",&M);for(int i=0;i<M;i++){int A,B,T;scanf("%d%d%d",&A,&B,&T);edges[A].push_back(Edge(B,T));edges[B].push_back(Edge(A,T));}for(int i=1;i<=N;i++){scanf("%d",&support[i]);}Dijstra(1);printf("%d\n",dis[2]==INF?-1:dis[2]);}return 0;
}

11.5 拓扑排序

方法

例子

11.5.1 例题 11.9 确定比赛名次

杭电-1285 hoj

拓扑排序代码模板

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>using namespace std;const int MAXN =500+10;vector<int>graph[MAXN];
int inDegree[MAXN];vector<int> TopologicalSort(int n){vector<int> togolopy;priority_queue<int,vector<int>,greater<int> >node;for(int i=1;i<=n;i++){if(inDegree[i]==0){node.push(i);}}while(!node.empty()){int u=node.top();node.pop();togolopy.push_back(u);for(int i=0;i<graph[u].size();i++){int v=graph[u][i];inDegree[v]--;if(inDegree[v]==0){node.push(v);}}}return togolopy;
}int main(){int n,m;while(scanf("%d%d",&n,&m)!=EOF){memset(graph,0,sizeof(graph));memset(inDegree,0,sizeof(inDegree));while(m--){int from,to;scanf("%d%d",&from,&to);graph[from].push_back(to);inDegree[to]++;}vector<int> answer=TopologicalSort(n);for(int i=0;i<answer.size();i++){if(i==0){printf("%d",answer[i]);}else{printf(" %d",answer[i]);}}printf("\n");}return 0;
}

11.5.2 例题 11.8 Legal or Not(拓扑排序判环)

题目描述
ACM-DIY is a large QQ group where many excellent acmers get together. It is so harmonious that just like a big family. Every day,many “holy cows” like HH, hh, AC, ZT, lcc, BF, Qinz and so on chat on-line to exchange their ideas. When someone has questions, many warm-hearted cows like Lost will come to help. Then the one being helped will call Lost “master”, and Lost will have a nice “prentice”. By and by, there are many pairs of “master and prentice”. But then problem occurs: there are too many masters and too many prentices, how can we know whether it is legal or not?
We all know a master can have many prentices and a prentice may have a lot of masters too, it’s legal. Nevertheless,some cows are not so honest, they hold illegal relationship. Take HH and 3xian for instant, HH is 3xian’s master and, at the same time, 3xian is HH’s master,which is quite illegal! To avoid this,please help us to judge whether their relationship is legal or not.
Please note that the “master and prentice” relation is transitive. It means that if A is B’s master ans B is C’s master, then A is C’s master.

Input
The input consists of several test cases. For each case, the first line contains two integers, N (members to be tested) and M (relationships to be tested)(2 <= N, M <= 100). Then M lines follow, each contains a pair of (x, y) which means x is y’s master and y is x’s prentice. The input is terminated by N = 0.
TO MAKE IT SIMPLE, we give every one a number (0, 1, 2,…, N-1). We use their numbers instead of their names.

Output
For each test case, print in one line the judgement of the messy relationship.
If it is legal, output “YES”, otherwise “NO”.

Sample Input
3 2
0 1
1 2
2 2
0 1
1 0
0 0
Sample Output
YES
NO

题目分析

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>using namespace std;const int MAXN=500;vector<int>graph[MAXN];
int inDegree[MAXN];//入度bool TopologicalSort(int n){queue<int> node;for(int i=0;i<n;i++){if(inDegree[i]==0){node.push(i);}}int number=0;//拓扑序列顶点个数while(!node.empty()){int u=node.front();node.pop();number++;for(int i=0;i<graph[u].size();i++){int v=graph[u][i];inDegree[v]--;if(inDegree[v]==0){node.push(v);}}}return n==number;
}
int main(){int n,m;while(scanf("%d%d",&n,&m)!=EOF){if(n==0&&m==0){break;}memset(graph,0,sizeof(graph));memset(inDegree,0,sizeof(inDegree));while(m--){int from,to;scanf("%d%d",&from,&to);graph[from].push_back(to);inDegree[to]++;}if(TopologicalSort(n)){printf("YES\n");}else{printf("NO\n");}}return 0;
}

11.6 关键路径

区别
关键路径

11.6.1 例题11.10 Instructions Arrangement

hdu - 4109

分析

关键路径模板代码

#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <climits>using namespace std;const int MAXN=1001;
const int INF=INT_MAX;struct Edge{int to;//终点int length;//距离Edge(int t,int l):to(t),length(l){}
};vector<Edge> graph[MAXN];
int earliest[MAXN];//最早开始时间
int latest[MAXN];//最晚开始时间
int inDegree[MAXN];//入度void CriticalPath(int n){vector<int> topology;//拓扑序列queue<int> node;for(int i=0;i<n;i++){if(inDegree[i]==0){node.push(i);earliest[i]=1;//初始化为1}}while(!node.empty()){int u=node.front();topology.push_back(u);node.pop();for(int i=0;i<graph[u].size();i++){int v=graph[u][i].to;int length=graph[u][i].length;earliest[v]=max(earliest[v],earliest[u]+length);inDegree[v]--;if(inDegree[v]==0){node.push(v);}}}for(int i=topology.size()-1;i>=0;i--){//求解最迟开始时间int u=topology[i];if(graph[u].size()==0){latest[u]=earliest[u];//汇点的最晚开始时间初始化}else{latest[u]=INF;//非汇点的最晚开始时间初始化}for(int j=0;j<graph[u].size();j++){int v=graph[u][j].to;int len=graph[u][j].length;latest[u]=min(latest[u],latest[v]-len);}}
}
int main(){int n,m;while(scanf("%d%d",&n,&m)!=EOF){memset(graph,0,sizeof(graph));memset(earliest,0,sizeof(earliest));memset(latest,0,sizeof(earliest));memset(inDegree,0,sizeof(inDegree));while(m--){int from,to,length;scanf("%d%d%d",&from,&to,&length);graph[from].push_back(Edge(to,length));inDegree[to]++;}CriticalPath(n);int answer=0;for(int i=0;i<n;i++){answer=max(answer,earliest[i]);}printf("%d\n",answer);}return 0;
}

说明:对于最早时间的初始化,如果题目没有特殊要求,那么所有结点都初始化为0,对于最晚时间的初始化,如果题目没有特殊要求,那么汇点最晚开始时间初始化为它的最早开始时间,其他节点初始化为INF

11.7 小结

王道考研系列 计算机考研 ——机试指南(第二版) 笔记(一)相关推荐

  1. 王道计算机考研机试指南二刷笔记-自用8

    目录 写在开头 第3章 排序与查找 3.1 排序 总结 3.2 查找 总结 写在开头 一刷到后面断更了,发现每题都写太浪费时间了. 一刷总结:数学问题.贪心.递归分治.数据结构二基本都刷完了,图论只刷 ...

  2. 考研机试准备--《王道论坛机试指南》学习笔记

    一.代码能力培养的层次结构 1.会编写(默写)经典程序的代码. 2.将自己的想法和抽象的问题转换为代码实现. 3.编写出的代码在大量的,多种多样的测试用例之前仍然具有健壮性. 二.常见概念 1.特殊判 ...

  3. 王道计算机考研机试指南刷题笔记-自用

    | 写在开头 前两周磨磨唧唧把第二章暴力求解枚举部分的例题和习题都AC了,思路都不太难,找到规律很快就能写出来,想着一定要写刷题笔记,把coding时遇到的困难都记录下来,方便以后复习,却也一直未曾动 ...

  4. 王道计算机考研机试指南刷题笔记-自用3

    | 2.2 模拟 3.其他模拟 例题2.9 KY25(清华大学复试上机题) 剩下的树__牛客网 (nowcoder.com) 1.总结 遇到的坑:直接把给出的区间右端点值减去左端点值+1得到这个区间移 ...

  5. 王道计算机考研机试指南刷题笔记-自用7

    目录 例题4.4 统计字符(浙大复试上机题) 例题4.5 字母统计(上交复试上机题) 第5章 数据结构一 5.1 向量 例题5.1 完数与盈数(清华复试上机题) 5.2 队列 例题5.2 约瑟夫问题N ...

  6. 机试指南第二章-经典入门-排版例题自解

    例2.8 叠筐 解题思路 这题的思路很有启发性,先排版后输出,及时阻止了我动规. AC代码 #include<cstdio> #include<iostream>using n ...

  7. 机试指南第二章-经典入门-查找例题自解

    查找: 对于查找问题,有难有易.可能只是直接地对某个数字的查找,也可能涉及搜索等相对难度更大的算法.这里先介绍查找的基础概念和方法. 例 2.9 找 x AC代码: #include<cstri ...

  8. Java算法:华为机试算法第二版(上),华为算法Java版

    牛客网华为机试练习题 文章目录 牛客网华为机试练习题 动态规划问题详解 前言 生活中的动态规划 2. 几个简单的概念 3. DP的典型应用:DAG最短路 4. 对DP原理的一点讨论 5. 例题:最长上 ...

  9. Java算法:华为机试算法第二版(中),华为算法Java版

    牛客网-华为机试练习题 34 题目描述:图片整理 Lily上课时使用字母数字图片教小朋友们学习英语单词,每次都需要把这些图片按照大小(ASCII码值从小到大)排列收好.请大家给Lily帮忙,通过C语言 ...

  10. 王道出版的机试指南_《王道论坛计算机考研机试指南》试读版.pdf

    <王道论坛计算机考研机试指南>试读版 王道论坛 王道论坛计算机考研机试指南 王道论坛 2013.01.06 写在前面的话 各位王道的小崽子们,今天你们考完初试了,感觉解放了吧?轻松了吧?无 ...

最新文章

  1. lua 函数调用1 -- 闭包详解和C调用
  2. module._init_() takes at most 2 arguments (3 given) (scrapy tutorial w/ xpath)
  3. python爬虫流程-Python爬虫流程
  4. Eclipse Color Theme
  5. It's OpenCV
  6. 透过认知智能剖析商业本质(iPIN CEO杨洋)丨硬创公开课
  7. Python的from import和import的区别?
  8. boost::math模块使用根查找的示例
  9. 牛客 - 字典序(思维)
  10. HashMap源代码深入剖析
  11. JVM调优总结(3):垃圾回收面临的问题
  12. 你不知道的Chrome调试技巧
  13. 直播电商源码,无加密
  14. php微信推送的模板信息内容都是空,微信模板消息 推送成功 但是内容显示空白...
  15. 基于51单片机的红外遥控器设计
  16. Linux------进程概念、进程控制
  17. This Week in Spring - July 15, 2013
  18. SaaS系统权限体系设计
  19. 【排序算法】冒泡排序|选择排序|插入排序|希尔排序
  20. Strtok分割字符串

热门文章

  1. 19.深入浅出:正弦波振荡电路——参考《模拟电子技术基础》清华大学华成英主讲
  2. 20.深入浅出:功率放大电路——参考《模拟电子技术基础》清华大学华成英主讲
  3. 微软官方office教程和微软官方office模板
  4. GNS3中下载路由器
  5. 笨方法学Python
  6. 即时网络通讯系统的设计与实现(QQ)
  7. 模板方法模式-Template Method Pattern
  8. openwrt qca9886 ath10k只能连32台设备问题解决
  9. word转pdf公式乱码_求助:Word转pdf时公式会公式变成乱码
  10. 大学计算机基础试题 百度网盘,【分享】《大学计算机基础》试题题库及答案 ~~~~~~~~~~~...