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

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

机试指南一些笔记。。。
题目代码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 二进制数(北京邮电大学复试上机题)

链接: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=0
        answer.push_back(0);
    }else{
        while(number!=0){//不断对2取模,求出二进制最末尾数字,随后除2
            answer.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=0
        answer.push_back('0');
    }else{
        while(number!=0){//不断对2取模,求出二进制最末尾数字,随后除2
            answer.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);//去除0x
        ConvertM2T(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=0
        answer.push_back(0);
    }else{
        while(number!=0){//不断对2取模,求出二进制最末尾数字,随后除2
            answer.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);//上界为根号n
    for(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 约数的个数

题目链接

分析1
例子
每个质因子都有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;//存储一下i
        for(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;	//数组最后面放置终点,价格为0
        st[n].dis = D;		//终点距离为D
        sort(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单独输出2
                printf("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()。队列是front
        sum+=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();//注意这里是top
                    myPriorityQueue.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(){                //初始化函数  父节点默认设为自己,高度默认为0
    for(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的辈分高,输出parent
            int 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的辈分低,输出child
            int 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; // 合并并累加k
        r[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;//无穷大或者0x7FFFFFFF

struct 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;//无穷大或者0x7FFFFFFF

struct 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,不是1
                cost[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,不是1
                cost[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-1
                dis[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 小结

在这里插入图片描述

  • 28
    点赞
  • 89
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值