2020-2021-2学期<算法分析与设计> 期末考试题解

A.简单计算

题目描述
键盘输入两个数,打印它们的和。
输入
共1行,有两个数(中间用空格隔开),分别表示两个加数。
输出
共1行,输出两个数的和。
样例输入

3 5

样例输出

8

解题思路:
签到题a+b,但是要注意数据范围,这个题用int可能会爆,注意用long long输入

A.cpp

#include<bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	LL a,b;
	cin >> a >> b;
	cout << a + b;
	
	return 0;
}

A.java

import java.util.Scanner;
public class Main {

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        System.out.println(cin.nextLong() + cin.nextLong());
        cin.close();
    }
}

B.排序

题目描述
将四个整数进行从小到大的顺序排列, 每一个数 范围[0,10000]

输入
四个整数

输出
从小到大输出这四个数,每两个数用一个空格隔开,行末没有空格。

样例输入

5 3 4 2

样例输出

2 3 4 5

解题思路:
简单的排序,可以直接调用排序函数轻松AC

B.cpp

#include<bits/stdc++.h>
using namespace std;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int a[4];
	for(int i=0;i<4;i++)cin >> a[i];
	sort(a,a+4);
	for(int i=0;i<4;i++)cout << a[i] << " ";
	
	return 0;
}

B.java

import java.util.Arrays;
import java.util.Scanner;
public class Main {

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        int []a = new int[4];
        for(int i=0;i<4;i++){
            a[i] = cin.nextInt();
        }
        Arrays.sort(a);
        for(Integer i:a){
            System.out.print(i + " ");
        }
        cin.close();
    }
}

C.后缀排序

题目描述
对于一个字符串,将其后缀子串进行排序,例如grain
其子串有:
grain
rain
ain
in
n
然后对各子串按字典顺序排序,即:
ain,grain,in,n,rain

输入
多组输入,每组输入为一行字符串。

输出
将子串排序输出,每行一个字符串

样例输入

grain
banana

样例输出

ain
grain
in
n
rain
a
ana
anana
banana
na
nana

解题思路:
这个题可以遍历,一个字符串的子串个数就是这个字符串的长度,先将字符串分解为若干个子串,然后调用sort函数进行排序就能顺利AC。

C.cpp

#include<bits/stdc++.h>
using namespace std;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	string s;
	while(cin >> s){
		string a[s.length()];
		int i = 0;
		for(int i=0;i<s.length();i++){
			a[i] = s.substr(i,s.length() - i);//分解成s.length()个子串
		}
		sort(a,a+s.length());
		for(auto &i:a){
			cout << i << "\n";
		}
	}
	
	return 0;
}

C.java

import java.util.Arrays;
import java.util.Scanner;
public class Main {

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        while(cin.hasNext()){
            String s = cin.next();
            String [] a = new String[s.length()];
            for(int i=0;i<s.length();i++){
                a[i] = s.substring(i,s.length());
            }
            Arrays.sort(a);
            for(String i:a){
                System.out.println(i);
            }
        }
        cin.close();
    }
}

D. 数学猜想

题目描述
著名的哥德巴赫猜想可以表述为:一个不小于6的偶数总可以写成两个素数和,比如8=5+3;爱好思考数学的老赵,发现了一个新的的数学猜想:
对任何一个不小于6的整数 n 似乎都可以拆成 三个素数 p,q,r的和 ,比如6=2+2+2,7=2+2+3,15=3+5+7=5+5+5,输出的三个素数 p,q,r 满足 p+q+r=n
输入
一个不小于6的整数n, 6≤n≤2∗109
输出
输出的三个素数 p,q,r 满足 p+q+r=n
样例输入

15

样例输出

5 5 5

解题思路:
最简单的方法就是构造法,可以直接构造出2,p,q和3,p,q的组合,然后直接遍历求出符合题目要求的p和q即可。

#include<bits/stdc++.h>
using namespace std;
using LL = long long;
const int maxn = 1e8;
bool prime(int n){
	if(n < 2)return false;
	if(n == 2)return true;
	if(n % 2 == 0)return false;
	for(int i=3;i*i<=n;i+=2){
		if(n % i == 0)return false;
	}
	return true;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	LL n;
	cin >> n;
	if(n % 2 == 0){
		n -= 2;
		cout << 2 << " ";
	} else {
		n -= 3;
		cout << 3 << " ";
	}
	for(int i=2;i<=n;i++){
		if(prime(i) && prime(n-i)){
			cout << i << " " << n - i;
			break;
		}
	}
	
	return 0;
}

D.java

import java.util.Scanner;

public class Main{
    static boolean isprime(int n){
        if(n < 2)return false;
        if(n == 2)return true;
        if(n % 2 == 0)return false;
        for(int i=3;i*i<=n;i+=2){
            if(n % i == 0)return false;
        }
        return true;
    }
    public static void main(String[] args) {
        Scanner cin=new Scanner(System.in);
        int n = cin.nextInt();
        if(n % 2 == 0){
            System.out.print(2 + " ");
            n -= 2;
        } else {
            System.out.print(3 + " ");
            n -= 3;
        }
        for(int i=2;i<=n;i++){
            if(isprime(i) && isprime(n-i)){
                System.out.print(i + " ");
                System.out.println(n - i);
                break;
            }
        }
        cin.close();
    }
}

E.方格取数

题目描述
设有N*N的方格图(N< =10),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。
某人从图的左上角的A 点(1,1)出发,可以向下行走,也可以向右走,直到到达右下角的B点(N,N)。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从A点到B 点共走两次,试找出2条这样的路径,使得取得的数之和为最大。

输入
输入的第一行为一个整数N(表示N*N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。

输出
只需输出一个整数,表示2条路径上取得的最大的和。

样例输入

8
2 3 13
2 6 6
3 5 7
4 4 14
5 2 21
5 6 4
6 3 15
7 2 14
0 0 0

样例输出

67

解题思路:
在这里插入图片描述

这个题我们的dp[i][j][h][k]:表示两条路同时走,第一条路径走到(i,j)时,第二条走到(h,k)时的最大数字和;
一个位置的值只能被取一次,那么当两次走到同一个位置时,只需要加格子上的值即可。当在不同位置,那两个位置的值都得加上。

E.cpp

#include<bits/stdc++.h>
using namespace std;
int n;
int a[100][100];
int sum;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cin >> n;
	memset(a,0,sizeof(a));
	int x,y,z;
	while(cin >> x >> y >> z){
		if(x == 0 && y == 0 && z == 0)break;
		a[x][y] = z;
	}
	int dp[n+2][n+2][n+2][n+2];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(int h=1;h<=n;h++){
				for(int k=1;k<=n;k++){
					int max1 = max(dp[i-1][j][h-1][k],dp[i][j-1][h][k-1]);
					int max2 = max(dp[i-1][j][h][k-1],dp[i][j-1][h-1][k]);
					dp[i][j][h][k] = max(max1,max2) + a[i][j];
					if(i!=h && j!=k){
						dp[i][j][h][k] += a[h][k];
					}
				}
			}
		}
	}
	cout << dp[n][n][n][n];
	
	return 0;
}

E.java

import java.util.Scanner;
public class Main {

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        int n = cin.nextInt();
        int [][]a = new int[n+10][n+10];
        while(cin.hasNext()){
            int x = cin.nextInt();
            int y = cin.nextInt();
            int z = cin.nextInt();
            if(x == 0 && y == 0 && z == 0)break;
            a[x][y] = z;
        }
        int [][][][]dp = new int[n+2][n+2][n+2][n+2];
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                for(int h=1;h<=n;h++){
                    for(int k=1;k<=n;k++){
                        int max1 = Math.max(dp[i-1][j][h-1][k],dp[i][j-1][h][k-1]);
                        int max2 = Math.max(dp[i][j-1][h-1][k],dp[i-1][j][h][k-1]);
                        dp[i][j][h][k] = Math.max(max1,max2) + a[i][j];
                        if(i != h && j != k){
                            dp[i][j][h][k] += a[h][k];
                        }
                    }
                }
            }
        }
        System.out.println(dp[n][n][n][n]);
        cin.close();
    }
}

F.拼接

题目描述
有一天阳阳摆弄着他最钟爱的木棒,他突发奇想,如果从中任意选取确定数量木棒,然后拼接成一根,可以得到多少长度不一的木棒呢?

输入
两行。
第 1 行,2 个正整数 N 和 M,分别表示阳阳共拥有木棒数和选出木棒的数量。
第 2 行,N 个 500 以内的正整数,表示各木棒的长度。

输出
一个整数,表示可以拼接出不同长度木棒的数量
样例输入

4 3
1 3 5 7

样例输出

4

解题思路:
一道深搜题,我们可以从n根小木棍中搜索出m条小木棍拼接出来的所有长度,然后利用set进行去重,最终set的长度就是答案。

F.cpp

#include<bits/stdc++.h>
using namespace std;
int n,m;
set <int> s;
int a[1000];
void dfs(int k,int sum,int cnt){
	if(cnt == m){
		s.insert(sum);
		return;
	}
	if(k == n)return;
	dfs(k+1,sum + a[k],cnt+1);
	dfs(k+1,sum,cnt);
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cin >> n >> m;
	for(int i=0;i<n;i++)cin >> a[i];
	dfs(0,0,0);
//	for(auto &i:s){
//		cout << i << " ";
//	}
	cout << s.size();
	
	return 0;
}

F.java

import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;

public class Main {
    static int n,m;
    static Set<Integer> s = new TreeSet<>();
    static int [] a = new int[100];
    static void dfs(int k,int cnt,int sum){
        if(cnt == m){
            s.add(sum);
            return;
        }
        if(k == n)return;
        dfs(k+1,cnt+1,sum + a[k]);
        dfs(k+1,cnt,sum);
    }
    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        n = cin.nextInt();
        m = cin.nextInt();
        for(int i=0;i<n;i++)a[i] = cin.nextInt();
        dfs(0,0,0);
        System.out.println(s.size());
        cin.close();
    }
}

G.采购大米

题目描述
目前全国都在饱受新型冠状病毒疫情的困扰,疫情当前我们最大的贡献就是闭门不出。同时各个社区也有社区志愿者战斗在”防疫一线”,帮助居民采购生活物资,在社区门口管理和登记车辆和行人的出口等。小创的妈妈就是社区志愿者中的一员,负责物资的采购,而今天要采购的物资是大米,小创的妈妈一共需要采买M斤大米,小创的妈妈已经拿到了N名大米供应商的信息,每个供应商的价格不同,每个供应商的供应量也不一定相同,小创的妈妈需要挑选出一些供应商来满足采买需求。
但是小创的妈妈比较头疼,若要采购足够量的大米(如果总的供应量不足,那么尽可能的购买最多),如何采购才能使得花费最小呢?请你帮助小创的妈妈来解决这个问题吧。

输入
第一行,两个整数N和M,N表示的是大米供应商的数量,M表示需要采买的大米总量。
1≤N≤1000,0≤M≤10^6
接下来N行,是用空格隔开的一个小数和一个整数,分别表示第i个供应商的单价xi和这个供应商的供应量yi
1≤Xi≤10,0≤yi≤10^6

输出
一行,一个小数,表示小创妈妈要采购M斤大米所需要的最小花费,小数点后保留一位小数。
样例输入

5 1000
1.9 100
2.1 200
1.8 300
2.2 800
2.5 400

样例输出

2030.0

提示
需要采购的代码是1000斤,1.8元的供应商采购300斤,1.9元的供应商采购100斤,2.1元的供应商采购200斤,2.2元的供应商采购400斤,总花费是2030.0元

解题思路:
贪心思想,价格便宜的大米优先购买,结构体排序,用最少的价格把米尽可能的买到最多。

G.cpp

#include<bits/stdc++.h>
using namespace std;
struct rice{
	double price;
	int sum;
}a[1000005];
bool mycmp(rice &m,rice &n){
	return m.price < n.price;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int n,m;
	cin >> n >> m;
	double ans = 0;
	for(int i=0;i<n;i++){
		cin >> a[i].price >> a[i].sum;
	}
	sort(a,a+n,mycmp);
	for(int i=0;i<n;i++){
		if(m <= 0)break;
		ans += a[i].price * min(m,a[i].sum);
		m -= a[i].sum;
	}
	cout << fixed << setprecision(1) << ans;
	return 0;
}

G.java

import java.util.*;

class rice{
    double price;
    int sum;
    rice(double price,int sum){
        this.price = price;
        this.sum = sum;
    }
}
public class Main {

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        int n = cin.nextInt();
        int m = cin.nextInt();
        rice []a = new rice[n];
        for(int i=0;i<n;i++){
            a[i] = new rice(cin.nextDouble(),cin.nextInt());
        }
        Arrays.sort(a, new Comparator<rice>() {
            @Override
            public int compare(rice o1, rice o2) {
                return (int)(o1.price*100) - (int)(o2.price*100);
            }
        });
        double ans = 0;
        for(int i=0;i<n;i++){
            if(m <= 0)break;
            ans += a[i].price * Math.min(m,a[i].sum);
            m -= a[i].sum;
        }
        System.out.printf("%.1f",ans);
        cin.close();
    }
}

H.连通图

题目描述
给定一个无向图和其中的所有边,判断这个图是否所有顶点都是连通的。
输入
第一行是两个整数 n 和 m(0<=n<=1000)。n 表示图的顶点数目,m 表示图中边的数目。
随后有 m 行数据,每行有两个值 x 和 y(0<x, y <=n),表示顶点 x 和 y 相连,顶点的编号从 1 开始计算。输入不保证这些边是否重复。

输出
对于每组输入数据,如果所有顶点都是连通的,输出"YES",否则输出"NO"。
样例输入

5 4
1 2
1 3
1 4
2 5

样例输出

YES

解题思路:
这道题我们可以利用并查集,通过并查集最终找根节点,若只有一个根节点说明图是连通的,否则是不连通的。

H.cpp

#include<bits/stdc++.h>
using namespace std;
int f[10000];
void init(int n) {
    for (int i = 1; i <= n; i++) {
        f[i] = i;
    }
}
int find(int x) {
    if (x == f[x]) {
        return x;
    }
    else {
        return f[x] = find(f[x]);
    }
}
/*
int find(int x){
  x==f[x]?return x:return find(f[x]);
}
*/
void merge(int x, int y) {
    int tx = find(x);
    int ty = find(y);
    if (ty != tx) {
        f[tx] = ty;
    }
}
int main() {
    ios :: sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int n, m;
    cin >> n >> m;
    init(n);
    while (m--) {
        int a, b;
        cin >> a >> b;
        merge(a, b);
    }
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        if (f[i] == i) {
            cnt++;
        }
    }
    if (cnt == 1) {
        cout << "YES";
    }
    else {
        cout << "NO";
    }
    return 0;
}

I.畅通工程

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

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

输出
对每个测试用例,在1行里输出最少还需要建设的道路数目。

样例输入

5 3
1 2
3 2
4 5
0

样例输出

1

解题思路:
并查集板子题

I.cpp

#include<bits/stdc++.h>
using namespace std;
int f[1000];
void init(int n) {
    for (int i = 1; i <= n; i++) {
        f[i] = i;
    }
}
int find(int x) {
    if (x == f[x]) {
        return x;
    }
    else {
        return f[x] = find(f[x]);//路径压缩
    }
}
/*
int find(int x){
  x==f[x]?return x:return find(f[x]);
}
*/
void merge(int x, int y) {
    int tx = find(x);
    int ty = find(y);
    if (ty != tx) {
        f[tx] = ty;//合并
    }
}
int main() {
    ios :: sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, m;
    while (cin >> n) {
        if (n == 0) {
            break;
        }
        cin >> m;
        init(n);
        while (m--) {
            int a, b;
            cin >> a >> b;
            merge(a, b);
        }
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            if (f[i] == i) {
                ans++;
            }
        }
        cout << ans - 1 << endl;
    }
    return 0;
}

I.java

import java.util.*;


public class Main {
    static int []f = new int[10000];
    static void init(int n) {
        for (int i = 1; i <= n; i++) {
            f[i] = i;
        }
    }
    static int find(int x) {
        if (x == f[x]) {
            return x;
        }
        else {
            return f[x] = find(f[x]);
        }
    }
    static void merge(int x, int y) {
        int tx = find(x);
        int ty = find(y);
        if (ty != tx) {
            f[tx] = ty;
        }
    }
    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        while(cin.hasNext()){
            int n = cin.nextInt();
            if(n == 0)break;
            int m = cin.nextInt();
            init(n);
            while (m-- != 0) {
                int a = cin.nextInt();
                int b = cin.nextInt();
                merge(a, b);
            }
            int ans = 0;
            for(int i=1;i<=n;i++){
                if(f[i] == i)ans++;
            }
            System.out.println(ans - 1);
        }
        cin.close();
    }
}

版权声明:本文为liupang14159原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。