[N诺] 浙江大学历年机试题解

1、统计字符

 统计一个给定字符串中指定的字符出现的次数。

测试输入包含若干测试用例,每个测试用例包含2行,第1行为一个长度不超过5的字符串,第2行为一个长度不超过80的字符串。注意这里的字符串包含空格,即空格也可能是要求被统计的字符之一。当读到'#'时输入结束,相应的结果不要输出。
#include<bits/stdc++.h>
using namespace std;

int main()
{
	string c;
    string n;
    while(c[0] != '#'){
        getline(cin,c);
        getline(cin,n);
        int cnt = 0;
        int j = 0;
        for(int i=0;i<c.length();i++){
            for(j=0;j<n.length();j++){
                if(c[i]==n[j])
                    cnt++;
            }
            if(cnt!=0){
                cout << c[i] << " " << cnt << endl;
                cnt = 0;
            }
        }
    }
}

2、畅通工程

省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。经过调查评估,得到的统计表中列出了有可能建设公路的若干条道路的成本。现请你编写程序,计算出全省畅通需要的最低成本。

测试输入包含若干测试用例。每个测试用例的第1行给出评估的道路条数 N、村庄数目M (N, M < =100 );随后的 N 行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。

最小生成树问题

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

int n,m;
const int max_n = 100;

struct node{
    int b,e,w;
}edges[max_n];

bool cmp(node n1,node n2){
    return n1.w<n2.w;
}

int S[max_n]; // 并查集
int find_root(int x){
    while(S[x]>0){
        x = S[x];
    }
    return x;
}

int kruskal()
{
    sort(edges,edges+n,cmp);
    
    int sum = 0; // 最小生成树的权值
    
    for(int i = 0;i<n;i++){
        // 最小边
        node now = edges[i];
        // 若不成环可以加入
        int fb = find_root(now.b);
        int fe = find_root(now.e);
        if(fb!=fe){
            S[fb]=fe;
            sum = sum+now.w;
            m--;
        }
    }
    if(m==1)
        return sum;
    else
        return -1;
}

int main(){
    while(cin>>n>>m){
        if(n==0) break; // 结束条件
        memset(S,0,sizeof(S)); // 全初始化为-1或0
        for(int i=0;i<n;i++){
            cin>>edges[i].b>>edges[i].e>>edges[i].w;
        }
        int ans = kruskal();
        if(ans==-1){
            cout<<"?"<<endl;
        }else cout << ans <<endl;
    }
}

3、畅通工程2

某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?

测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。 
    注意:两个城市之间可以有多条道路相通,也就是说
    3 3
    1 2
    1 2
    2 1
    这种输入也是合法的
    当N为0时,输入结束,该用例不被处理。

并查集的应用

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

int n,m;
const int max_n = 1000;

int S[max_n];

int find_root(int x){
    while(S[x]>0){
        x = S[x];
    }
    return x;
}

int main(){
    while(cin>>n>>m) {
        if (n == 0) break;
        memset(S, 0, sizeof S);
        int sum = 0;
        for(int i =0;i<m;i++){
            int x,y;
            cin >> x >> y;
            int fx = find_root(x);
            int fy = find_root(y);
            if(fx!=fy){
                S[fx] = fy;
                sum++;
            }
        }
        cout << n-sum-1 << endl;
    }
}

4.继续畅通工程

省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。现得到城镇道路统计表,表中列出了任意两城镇间修建道路的费用,以及该道路是否已经修通的状态。现请你编写程序,计算出全省畅通需要的最低成本。

测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( 1< N < 100 );随后的 N(N-1)/2 行对应村庄间道路的成本及修建状态,每行给4个正整数,分别是两个村庄的编号(从1编号到N),此两村庄间道路的成本,以及修建状态:1表示已建,0表示未建。

当N为0时输入结束。
#include<bits/stdc++.h>
using namespace std;

const int max_n=100;
int n;
struct node{
    int u,v,w;
    int flag;
}edges[max_n];

int cmp(node n1, node n2){
    return n1.w < n2.w;
}

int S[max_n];
int find_root(int x){
    while(S[x]>0){
        x = S[x];
    }
    return x;
}

int kruskal(int m){
    sort(edges,edges+m,cmp);
    int sum = 0;
    int total = 0;
    for(int i=0;i<m;i++){
        node now = edges[i];
        int fa = find_root(now.u);
        int fb = find_root(now.v);
        if(total == n-1){
            break;
        }
        if(fa!=fb){
            sum = sum + now.w;
            S[fa] = fb;
            total++;
        }
    }
    return sum;
}

int main(){
    while(cin>>n){
        if(n==0) break;
        memset(S,-1,sizeof(S));
        int m = n*(n-1)/2;
        for(int i=0;i<m;i++){
            cin>>edges[i].u>>edges[i].v>>edges[i].w>>edges[i].flag;
            if(edges[i].flag==1) edges[i].w=0;
        }
        int ret = kruskal(m);
        cout << ret << endl;
    }
}

5.二叉搜索树

判断两序列是否为同一二叉搜索树序列

开始一个数n,(1<=n<=20) 表示有n个需要判断,n= 0 的时候输入结束。
接下去一行是一个序列,序列长度小于10,包含(0~9)的数字,没有重复数字,根据这个序列可以构造出一颗二叉搜索树。
接下去的n行有n个序列,每个序列格式跟第一个序列一样,请判断这两个序列是否能组成同一颗二叉搜索树。
#include<bits/stdc++.h>
using namespace std;

typedef struct node{
    char data; // 注意是以字符形式建树
    struct node *lchild,*rchild;
}*BitTree;

vector<char> right_ans,each_ans;
void InsertBitTree(BitTree &T,char x){
    if(T==NULL){
        T = new node;
        T->data = x;
        T->lchild = NULL;
        T->rchild = NULL;
        return; // 递归终止
    }else if(T->data > x){
        InsertBitTree(T->lchild,x);
    }else if(T->data < x){
        InsertBitTree(T->rchild,x);
    }
}

void PreOrderTraverse(BitTree T) {
    if (T == NULL) return;
    each_ans.push_back(T->data);
    PreOrderTraverse(T->lchild);
    PreOrderTraverse(T->rchild);
}

int main(){
    int n;
    string right,each;
    while(cin>>n){
        if(n==0) return 0;
        cin >> right;
        BitTree T = NULL;
        for(int i = 0;i<right.size();i++){
            InsertBitTree(T,right[i]);
        }
        PreOrderTraverse(T);
        for (int i = 0; i < each_ans.size(); ++i) right_ans.push_back(each_ans[i]); // 函数里默认写的是each_ans
        while(n--){
            each_ans = {};
            cin>>each;
            BitTree T = NULL;
            for(int i = 0;i<each.size();i++){
                InsertBitTree(T,each[i]);
            }

            PreOrderTraverse(T);
            int flag = 1;
            for(int i=0;i<each.size();i++){
                if(right_ans[i]!=each_ans[i]){
                    flag = 0;
                    break;
                }
            }
            if(flag==1){
                cout << "YES" << endl;
            }else{
                cout << "NO" << endl;
            }
        }
    }
}

6.统计同成绩学生人数

读入N名学生的成绩,将获得某一给定分数的学生人数输出。

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


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

当读到N=0时输入结束。其中N不超过1000,成绩分数为(包含)0到100之间的一个整数。
#include<bits/stdc++.h>
using namespace std;

int main(){
	int n;
	while(cin>>n){
		if(n==0) return 0;
		map<int,int> a;
		int grade,aim;
		while(n--){
			cin>>grade;
			a[grade]++;
		}
		cin>>aim;
		cout << a[aim] << endl;
	}
}

7.EXCEL排序

Excel可以对一组纪录按任意指定列排序。现请你编写程序实现类似功能。     对每个测试用例,首先输出1行“Case i:”,其中 i 是测试用例的编号(从1开始)。随后在 N 行中输出按要求排序后的结果,即:当 C=1 时,按学号递增排序;当 C=2时,按姓名的非递减字典序排序;当 C=3 时,按成绩的非递减排序。当若干学生具有相同姓名或者相同成绩时,则按他们的学号递增排序。

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

const int max_N = 100000;
struct student{
    string No;
    string name;
    int grade;
}students[max_N];

bool cmp1(struct student stu1,struct student stu2){
    return stu1.No < stu2.No;
}

bool cmp2(struct student stu1,struct student stu2){
    if(stu1.name == stu2.name){
        return stu1.No < stu2.No;
    }else{
        return stu1.name < stu2.name;
    }
}

bool cmp3(struct student stu1,struct student stu2){
    if(stu1.grade == stu2.grade){
        return stu1.No < stu2.No;
    }else{
        return stu1.grade < stu2.grade;
    }
}

int main(){
    int N,C;
    cin >> N >> C;
    for(int i=0;i<N;i++){
        struct student stu;
        cin >> stu.No >> stu.name >> stu.grade;
        students[i] = stu;
    }
    if(C==1){
        sort(students,students+N,cmp1);
    }else if(C==2){
        sort(students,students+N,cmp2);
    }else if(C==3){
        sort(students,students+N,cmp3);
    }
    cout << "Case:"<<endl;
    for(int i=0;i<N;i++){
        cout << students[i].No << " "<<students[i].name<<" " << students[i].grade<<endl;
    }
}

8.最大连续子序列

给定K个整数的序列{ N1, N2, ..., NK },其任意连续子序列可表示为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= K。最大连续子序列是所有连续子序列中元素和最大的一个,例如给定序列{ -2, 11, -4, 13, -5, -2 },其最大连续子序列为{ 11, -4, 13 },最大和为20。现在增加一个要求,即还需要输出该子序列的第一个和最后一个元素。

测试输入包含若干测试用例,每个测试用例占2行,第1行给出正整数K( K< 10000 ),第2行给出K个整数,中间用空格分隔。当K为0时,输入结束,该用例不被处理。
#include<bits/stdc++.h>
using namespace std;

const int max_k = 10000;

int dp[max_k],dp_copy[max_k];
int main(){
    int k;
    while(cin>>k){
        int flag = 0; // 是否全为负数
        if(k==0) return 0;
        memset(dp,0,max_k);
        for(int i = 0;i<k;i++){
            cin>>dp[i];
            if(dp[i]>=0){
                flag = 1;
            }
            dp_copy[i] = dp[i];
        }
        if(flag){
            for(int i = 1;i<k;i++){
                dp[i] = max(dp[i-1]+dp[i],dp[i]);
            }
            int max_value =*max_element(dp,dp+k);
            int max_pos = distance(dp,max_element(dp,dp+k));

            int temp = max_value;
            int min_pos = max_pos;
            while(dp_copy[min_pos]!=temp){
                temp = temp - dp_copy[min_pos];
                min_pos--;
            }

            cout << max_value <<" "<<dp_copy[min_pos]<<" "<<dp_copy[max_pos]<<endl;
        }else{
            cout << 0 <<" "<<dp_copy[0]<<" "<<dp_copy[k-1]<<endl;
        }
    }
}

9.最短路径问题

给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。

输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点t。n和m为0时输入结束。
(1<n<=1000, 0<m<100000, s != t)

有n个集合(初始有n个点,一个点独自成一个集合),先选出一条最短的边,假设它属于集合u,然后从跟它邻接的边中选出一条最短的且不与集合u中已经存在的边形成回路的边,合并到集合u中,再从非u集合中选出一条与集合u中的边邻接且距离最短的边,加入到集合u中,以此类推。

Dijkstra是用来解决给定起点和重点求路径的问题。

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

const int MAX_N = 1000;

int visited[MAX_N]; // 是否已经访问过该点

int dist[MAX_N]; // dist[i]起点到i的距离
int cost[MAX_N]; // cost[i]起点到i的花费


struct Edge{
    int to;
    int length;
    int price;
    Edge(int i, int j, int k): to(i), length(j), price(k){}
};

vector<Edge> graph[MAX_N];


struct Point{
    int number;
    int distance;
    Point(int i, int j): number(i), distance(j){}
    bool operator< (const Point& p) const{ // 重定义操作符< 使得Point之间可比
        return distance > p.distance;
    }
};


void Dijkstra(int s, int n){ // s到n的最短路
    dist[s] = 0;
    cost[s] = 0;

    priority_queue<Point> q; // 优先队列
    q.push(Point(s, 0)); // 加入起点 起点到起点距离显然为0

    while(!q.empty()){ // 只要q不空
        int u = q.top().number;  // 队列头
        q.pop();

        if(visited[u] == 1){  // 已经被访问过
            continue;
        }
        visited[u] = 1; // 未被访问过 则标记位访问过

        for(int i=0; i<graph[u].size(); i++){ // 遍历与u邻接的边
            int v = graph[u][i].to;
            int uv_price = graph[u][i].price;
            int uv_len = graph[u][i].length;
            if(dist[u] + uv_len < dist[v] || (dist[u] + uv_len == dist[v] && cost[u] + uv_price < cost[v])){ // 直达距离更短或距离相同但花费更少
                // 更新从起点到v的距离
                dist[v] = dist[u] + uv_len;
                cost[v] = cost[u] + uv_price;
            }
            q.push(Point(v, dist[v])); // 加入v 下次循环找v的邻接...

        }
    }
}



int main(){
    int m, n;
    while(cin>>n>>m){
        if(n == 0 && m==0) break;

        memset(graph, 0, sizeof(graph));
        memset(cost, 0x3f, sizeof(cost)); // 0x3f表示无限大
        memset(dist, 0x3f, sizeof(dist)); // 0x3f表示无限大
        memset(visited, 0, sizeof(visited)); // 都是未访问

        for(int i=0; i<m; i++){
            int from, to, d, c;
            cin>>from>>to>>d>>c;
            graph[from].push_back(Edge(to, d, c));
            graph[to].push_back(Edge(from, d, c));
        }

        int s, t;
        cin>>s>>t;

        Dijkstra(s, n);

        cout<<dist[t]<<" "<<cost[t]<<endl;
    }

    return 0;
}

10.xxx定律

对于一个数n,如果是偶数,就把n砍掉一半;如果是奇数,把n变成 3*n+ 1后砍掉一半,直到该数变为1为止。     请计算需要经过几步才能将n变到1,具体可见样例。

测试包含多个用例,每个用例包含一个整数n,当n为0 时表示输入结束。(1<=n<=10000)
#include<bits/stdc++.h>
using namespace std;

int main(){
    int n;
    while(cin>>n){
        if(n==0) return 0;
        int cnt = 0;
        while(n!=1){
            if(n%2==0){
                n = n/2;
            }else{
                n = (3*n+1)/2;
            }
            cnt++;
        }
        cout << cnt << endl;
    }
}

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值