【PTA】2022年蓝桥杯及天梯赛赛前训练(C++练习)

前言

团体程序设计天梯赛-练习集 PTA上面的基础题,作为C++的小练习~~

L1-1 嫑废话上代码

Linux 之父 Linus Torvalds 的名言是:“Talk is cheap. Show me the code.”(嫑废话,上代码)。本题就请你直接在屏幕上输出这句话。

输入格式:
本题没有输入。

输出格式:
在一行中输出 Talk is cheap. Show me the code.
#include<iostream>
using namespace std;

int main()
{
    
    
	cout  << "Talk is cheap. Show me the code." << "";
}

L1-2 猫是液体

在这里插入图片描述

测量一个人的体积是很难的,但猫就不一样了。因为猫是液体,所以可以很容易地通过测量一个长方体容器的容积来得到容器里猫的体积。本题就请你完成这个计算。

输入格式:
输入在第一行中给出 3 个不超过 100 的正整数,分别对应容器的长、宽、高。

输出格式:
在一行中输出猫的体积。

输入样例:
23 15 20
输出样例:
6900
#include<iostream>
using namespace std;

int main()
{
    
    
	int a, b, c;
	cin >> a >> b >> c;
	cout  << a*b*c << "";
}

L1-3 洛希极限

科幻电影《流浪地球》中一个重要的情节是地球距离木星太近时,大气开始被木星吸走,而随着不断接近地木“刚体洛希极限”,地球面临被彻底撕碎的危险。但实际上,这个计算是错误的。
在这里插入图片描述

洛希极限(Roche limit)是一个天体自身的引力与第二个天体造成的潮汐力相等时的距离。当两个天体的距离少于洛希极限,天体就会倾向碎散,继而成为第二个天体的环。它以首位计算这个极限的人爱德华·洛希命名。(摘自百度百科)

大天体密度与小天体的密度的比值开 3 次方后,再乘以大天体的半径以及一个倍数(流体对应的倍数是 2.455,刚体对应的倍数是 1.26),就是洛希极限的值。例如木星与地球的密度比值开 3 次方是 0.622,如果假设地球是流体,那么洛希极限就是 0.622×2.455=1.52701 倍木星半径;但地球是刚体,对应的洛希极限是 0.622×1.26=0.78372 倍木星半径,这个距离比木星半径小,即只有当地球位于木星内部的时候才会被撕碎,换言之,就是地球不可能被撕碎。

本题就请你判断一个小天体会不会被一个大天体撕碎。

输入格式:
输入在一行中给出 3 个数字,依次为:大天体密度与小天体的密度的比值开 3 次方后计算出的值(≤1)、小天体的属性(0 表示流体、1 表示刚体)、两个天体的距离与大天体半径的比值(>1 但不超过 10)。

输出格式:
在一行中首先输出小天体的洛希极限与大天体半径的比值(输出小数点后2位);随后空一格;最后输出 ^_^ 如果小天体不会被撕碎,否则输出 T_T。

输入样例 10.622 0 1.4
输出样例 11.53 T_T
输入样例 20.622 1 1.4
输出样例 20.78 ^_^
import java.util.Scanner;

public class Main {
    
    
	public static void main(String[] args) {
    
    
		Scanner sc = new Scanner(System.in);
		double a = sc.nextDouble();
		int b = sc.nextInt();
		double c = sc.nextDouble();
		double d = 0;
		if (b == 0) {
    
    
			d = a*2.455;
			System.out.printf("%.2f ", d);
		} else {
    
    
			d = a*1.26;
			System.out.printf("%.2f ", d);
		}
		if (d < c) {
    
    
			System.out.println("^_^");
		} else {
    
    
			System.out.println("T_T");
		}
	}
}

L1-4 调和平均

N 个正数的算数平均是这些数的和除以 N,它们的调和平均是它们倒数的算数平均的倒数。本题就请你计算给定的一系列正数的调和平均值。

输入格式:
每个输入包含 1 个测试用例。每个测试用例第 1 行给出正整数 N (1000);第 2 行给出 N 个正数,都在区间 [0.1,100] 内。

输出格式:
在一行中输出给定数列的调和平均值,输出小数点后2位。

输入样例:
8
10 15 12.7 0.3 4 13 1 15.6
输出样例:
1.61
#include<bits/stdc++.h>
using namespace std;

int main()
{
    
    
	int a, d;
	double sum, b;
	cin >> a;
	d = a;
	while (d--)
	{
    
    
		cin >> b;
		sum += 1/b;
	}
	
	printf("%.2f", a/sum);
	
	return 0;
}

L1-5 胎压监测

小轿车中有一个系统随时监测四个车轮的胎压,如果四轮胎压不是很平衡,则可能对行车造成严重的影响。

在这里插入图片描述

让我们把四个车轮 —— 左前轮、右前轮、右后轮、左后轮 —— 顺次编号为 1234。本题就请你编写一个监测程序,随时监测四轮的胎压,并给出正确的报警信息。报警规则如下:

如果所有轮胎的压力值与它们中的最大值误差在一个给定阈值内,并且都不低于系统设定的最低报警胎压,则说明情况正常,不报警;
如果存在一个轮胎的压力值与它们中的最大值误差超过了阈值,或者低于系统设定的最低报警胎压,则不仅要报警,而且要给出可能漏气的轮胎的准确位置;
如果存在两个或两个以上轮胎的压力值与它们中的最大值误差超过了阈值,或者低于系统设定的最低报警胎压,则报警要求检查所有轮胎。
输入格式:
输入在一行中给出 6[0, 400] 范围内的整数,依次为 1~4 号轮胎的胎压、最低报警胎压、以及胎压差的阈值。

输出格式:
根据输入的胎压值给出对应信息:

如果不用报警,输出 Normal;
如果有一个轮胎需要报警,输出 Warning: please check #X!,其中 X 是出问题的轮胎的编号;
如果需要检查所有轮胎,输出 Warning: please check all the tires!。

输入样例 1242 251 231 248 230 20
输出样例 1:
Normal
输入样例 2242 251 232 248 230 10
输出样例 2:
Warning: please check #3!
输入样例 3240 251 232 248 240 10
输出样例 3:
Warning: please check all the tires!
#include<bits/stdc++.h>
using namespace std;

int a[6];
int main()
{
    
    
	int a_index = 0, aa = 0, c, max = 0;
	for (int i = 0; i < 4; i++)
	{
    
    
		cin >> a[i];
		max = a[i] > max ? a[i] : max;
	}
	cin >> a[4] >> a[5];
	
	for (int i = 0; i < 4; i++)
	{
    
    
		if (max-a[i] > a[5] || a[i] < a[4]) {
    
    
			aa++;
			a_index = i;
		}
	}
	if (aa == 0)
		cout << "Normal" << endl;
	else if (aa == 1)
		printf("Warning: please check #%d!", a_index+1);
	else
		cout << "Warning: please check all the tires!" << endl;
	
	return 0;
}

L1-6 吃火锅

在这里插入图片描述

以上图片来自微信朋友圈:这种天气你有什么破事打电话给我基本没用。但是如果你说“吃火锅”,那就厉害了,我们的故事就开始了。

本题要求你实现一个程序,自动检查你朋友给你发来的信息里有没有 chi1 huo3 guo1。

输入格式:
输入每行给出一句不超过 80 个字符的、以回车结尾的朋友信息,信息为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。当读到某一行只有一个英文句点 . 时,输入结束,此行不算在朋友信息里。

输出格式:
首先在一行中输出朋友信息的总条数。然后对朋友的每一行信息,检查其中是否包含 chi1 huo3 guo1,并且统计这样厉害的信息有多少条。在第二行中首先输出第一次出现 chi1 huo3 guo1 的信息是第几条(从 1 开始计数),然后输出这类信息的总条数,其间以一个空格分隔。题目保证输出的所有数字不超过 100。

如果朋友从头到尾都没提 chi1 huo3 guo1 这个关键词,则在第二行输出一个表情 -_-#。

输入样例 1:
Hello!
are you there?
wantta chi1 huo3 guo1?
that's so li hai le
our story begins from chi1 huo3 guo1 le
.
输出样例 15
3 2
输入样例 2:
Hello!
are you there?
wantta qi huo3 guo1 chi1huo3guo1?
that's so li hai le
our story begins from ci1 huo4 guo2 le
.
输出样例 25
-_-#
#include<bits/stdc++.h>
using namespace std;

int main()
{
    
    
	int i = 0, j = 0, index = 0;
	string a;
	do {
    
    
		i++;
		getline(cin, a);
		if (a.find("chi1 huo3 guo1") <= 80 && a.find("chi1 huo3 guo1") >= 0) {
    
    
			if (j == 0)
				index = i;
			j++;
		}
		
	} while (a != ".");
	cout << i-1 << endl;
	if (j == 0)
		cout << "-_-#" << endl;
	else 
		cout << index << " " << j << endl;
	return 0;
}

L1-7 前世档案

在这里插入图片描述
网络世界中时常会遇到这类滑稽的算命小程序,实现原理很简单,随便设计几个问题,根据玩家对每个问题的回答选择一条判断树中的路径(如下图所示),结论就是路径终点对应的那个结点。

在这里插入图片描述

现在我们把结论从左到右顺序编号,编号从 1 开始。这里假设回答都是简单的“是”或“否”,又假设回答“是”对应向左的路径,回答“否”对应向右的路径。给定玩家的一系列回答,请你返回其得到的结论的编号。

输入格式:
输入第一行给出两个正整数:N(≤30)为玩家做一次测试要回答的问题数量;M(≤100)为玩家人数。

随后 M 行,每行顺次给出玩家的 N 个回答。这里用 y 代表“是”,用 n 代表“否”。

输出格式:
对每个玩家,在一行中输出其对应的结论的编号。

输入样例:
3 4
yny
nyy
nyn
yyn
输出样例:
3
5
6
2
#include<bits/stdc++.h>
using namespace std;

int main()
{
    
    
	int i = 0, j = 0;
	cin >> i >> j;
	string s;
	int a, sum;
	while (j--)
	{
    
    
		int sum = 0;
		cin >> s;
		a = pow(2, i-1);
		for (int k = 0; k < sizeof(s); k++)
		{
    
    
			if (s[k] == 'n') {
    
    
				sum += a;
			}
			a /= 2;
		}
		cout << sum+1 << endl;
	}
	
	return 0;
}

Java:

import java.util.Scanner;

public class L1_7 {
    
    
    public static void main(String[] args) {
    
    
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        sc.nextLine();
        while (m-- > 0) {
    
    
            String fate = sc.nextLine();
            /*int ans = 0;
            for (int i = 0; i < fate.length(); i++) {
                ans *= 2;
                if (fate.charAt(i) == 'y')
                    ans += 0;
                else if (fate.charAt(i) == 'n')
                    ans += 1;
            }
            System.out.println(ans+1);*/

            // 解法二
            int root = 1;
            int t = (int) Math.pow(2, n);
            for (int i = 0; i < fate.length(); i++) {
    
    
                if (fate.charAt(i) == 'y')
                     root *= 2;
                else if (fate.charAt(i) == 'n')
                    root = root * 2 +1;
            }
            System.out.println(root-t+1);
        }
    }
}

L1-8 刮刮彩票

“刮刮彩票”是一款网络游戏里面的一个小游戏。如图所示:
在这里插入图片描述

每次游戏玩家会拿到一张彩票,上面会有 9 个数字,分别为数字 1 到数字 9,数字各不重复,并以 3×3 的“九宫格”形式排布在彩票上。

在游戏开始时能看见一个位置上的数字,其他位置上的数字均不可见。你可以选择三个位置的数字刮开,这样玩家就能看见四个位置上的数字了。最后玩家再从 3 横、3 竖、2 斜共 8 个方向中挑选一个方向,方向上三个数字的和可根据下列表格进行兑奖,获得对应数额的金币。

数字合计 获得金币 数字合计 获得金币
6 10,000 16 72
7 36 17 180
8 720 18 119
9 360 19 36
10 80 20 306
11 252 21 1,080
12 108 22 144
13 72 23 1,800
14 54 24 3,600
15 180

现在请你写出一个模拟程序,模拟玩家的游戏过程。

输入格式:
输入第一部分给出一张合法的彩票,即用 33 列给出 09 的数字。0 表示的是这个位置上的数字初始时就能看见了,而不是彩票上的数字为 0。

第二部给出玩家刮开的三个位置,分为三行,每行按格式 x y 给出玩家刮开的位置的行号和列号(题目中定义左上角的位置为第 1 行、第 1 列。)。数据保证玩家不会重复刮开已刮开的数字。

最后一部分给出玩家选择的方向,即一个整数: 13 表示选择横向的第一行、第二行、第三行,46 表示纵向的第一列、第二列、第三列,78分别表示左上到右下的主对角线和右上到左下的副对角线。

输出格式:
对于每一个刮开的操作,在一行中输出玩家能看到的数字。最后对于选择的方向,在一行中输出玩家获得的金币数量。

输入样例:
1 2 3
4 5 6
7 8 0
1 1
2 2
2 3
7
输出样例:
1
5
6
180
#include<bits/stdc++.h>
using namespace std;

int a[4][4];
int b[] = {
    
    10000, 36, 720, 360, 80, 252, 108, 72, 54, 180, 72, 180, 119, 36, 306, 1080, 144, 1800, 3600};
int main()
{
    
    
	int aa = 0, sum = 0, c, d;
	for (int i = 1; i < 4; i++) {
    
    
		for (int j = 1; j < 4; j++) {
    
    
			cin >> a[i][j];
			aa += a[i][j];
			if (a[i][j] == 0) 
			{
    
    
				c = i;
				d = j;
			}
		}
	}
	a[c][d] = 45 - aa;
	sum = 0;
	
	int x, y;
	for (int i = 1; i < 4; i++) {
    
    
		cin >> x >> y;
		cout << a[x][y] << endl;
	}
	cin >> x;
	if (x < 4)
	{
    
    
		for (int i = 1; i < 4; i++) 
		{
    
    
			sum += a[x][i];
		}
	} 
	else if (x < 7) 
	{
    
    
		for (int i = 1; i < 4; i++) 
		{
    
    
			sum += a[i][x-3];
		}
	} 
	else if (x == 7)
	{
    
    
		for (int i = 1; i < 4; i++) 
		{
    
    
			sum += a[i][i];
		}
	}
	else
	{
    
    
		for (int i = 1; i < 4; i++) 
		{
    
    
			sum += a[i][4-i];
		}
	}
	cout << b[sum-6] << endl;
	return 0;
}

L2-1 简单计算器

在这里插入图片描述
本题要求你为初学数据结构的小伙伴设计一款简单的利用堆栈执行的计算器。如上图所示,计算器由两个堆栈组成,一个堆栈S存放数字,另一个堆栈Sg存放运算符。计算器的最下方有一个等号键,每次按下这个键,计算器就执行以下操作:

  1. 从S1中弹出两个数字,顺序为n1和n2;
  2. 从S中弹出一个运算符op;
  3. 执行计算n2 op n1;
  4. 将得到的结果压回S1。

直到两个堆栈都为空时,计算结束,最后的结果将显示在屏幕上。

在这里插入图片描述

输入样例 15
40 5 8 3 2
/ * - +
输出样例 12
输入样例 25
2 5 8 4 4
* / - +
输出样例 2:
ERROR: 5/0
#include<bits/stdc++.h>
using namespace std;

int a[10010];
char c[10010];
int main()
{
    
    
	int n, top1 = -1, top2 = -1;
	cin >> n;
	for (int i = 0; i < n; i++) {
    
    
		cin >> a[++top1];
	}
	for (int i = 0; i < n-1; i++) {
    
    
		cin >> c[++top2];
	}
	int op1, op2;
	char cc;
	while (top2 > -1)
	{
    
    
		cc = c[top2--];
		op1 = a[top1--];
		op2 = a[top1--];
		if (cc == '+')
		{
    
    
			a[++top1] = op1+op2;
		}
		else if (cc == '-')
		{
    
    
			a[++top1] = op2-op1;
		}
		else if (cc == '*')
		{
    
    
			a[++top1] = op2*op1;
		}
		else
		{
    
    
			if (op1 == 0) {
    
    
				printf("ERROR: %d/0", op2);
				break;
			}
			a[++top1] = op2/op1;
		}
	}
	if (op1 != 0)
		cout << a[top1] << endl;
	return 0;
}

L2-2 口罩发放

为了抗击来势汹汹的 COVID19 新型冠状病毒,全国各地均启动了各项措施控制疫情发展,其中一个重要的环节是口罩的发放。

某市出于给市民发放口罩的需要,推出了一款小程序让市民填写信息,方便工作的开展。小程序收集了各种信息,包括市民的姓名、身份证、身体情况、提交时间等,但因为数据量太大,需要根据一定规则进行筛选和处理,请你编写程序,按照给定规则输出口罩的寄送名单。
输入第一行是两个正整数D和P(1<D,P<30),表示有D天的数据,市民两次获得口罩的时间至少需要间隔P天。
接下来D块数据,每块给出一天的申请信息。第i块数据(i= 1,.,D)的第一行是两个整数T;和S;(1<T,S,1000) ,表示在第i天有T条申请,总共有S;个口罩发放名额。随后T行,每行给出一条申请信息,格式如下:
姓名 身份证号 身体情况 提交时间
给定数据约束如下:

姓名 是一个长度不超过 10 的不包含空格的非空字符串;
身份证号 是一个长度不超过 20 的非空字符串;
身体情况 是 0 或者 10 表示自觉良好,1 表示有相关症状;
提交时间 是 hh:mm,为24小时时间(由 00:0023:59。例如 09:08。)。注意,给定的记录的提交时间不一定有序;
身份证号 各不相同,同一个身份证号被认为是同一个人,数据保证同一个身份证号姓名是相同的。

能发放口罩的记录要求如下:

身份证号 必须是 18 位的数字(可以包含前导0);
同一个身份证号若在第 i 天申请成功,则接下来的 P 天不能再次申请。也就是说,若第 i 天申请成功,则等到第 i+P+1 天才能再次申请;
在上面两条都符合的情况下,按照提交时间的先后顺序发放,直至全部记录处理完毕或Si个名额用完。如果提交时间相同,则按照在列表中出现的先后顺序决定。

输出格式:
对于每一天的申请记录,每行输出一位得到口罩的人的姓名及身份证号,用一个空格隔开。顺序按照发放顺序确定。

在输出完发放记录后,你还需要输出有合法记录的、身体状况为 1 的申请人的姓名及身份证号,用空格隔开。顺序按照申请记录中出现的顺序确定,同一个人只需要输出一次。
import java.util.*;

class People {
    
    
    String name;
    String id;
    boolean status;  // 0 : 自觉良好,  1 : 表示有相关症状
    int day;  // 第几天领的
    int time;  // 提交的时间
    boolean get = false;

    public People() {
    
    }

    public People(String name, String id, boolean status, int day, int time) {
    
    
        this.name = name;
        this.id = id;
        this.status = status;
        this.day = day;
        this.time = time;
    }

}

public class L2_2 {
    
    
    public static void main(String[] args) {
    
    
        Scanner sc = new Scanner(System.in);
        int d = sc.nextInt();  // D天数据
        int p = sc.nextInt();  // 市民两次获得口罩至少间隔天数, 第i+p+1天才可以申请
        Map<String, People> mapPeo = new HashMap<>();  // 总人数
        //Map<String, People> mapCur = new TreeMap<>();  // 当前人数
        List<People> curpeos = new ArrayList<>();
        List<String> error = new ArrayList<>();

        for (int j = 1; j <= d; j++) {
    
    
            int t = sc.nextInt();  // t条申请
            int s = sc.nextInt();  // s个口罩名额
            curpeos.clear();
            for (int i = 1; i <= t; i++) {
    
    // 获取一个人的信息
                String name = sc.next();
                String id = sc.next();
                boolean status = sc.nextInt() == 1 ? true : false;
                String time = sc.next();
                //System.out.println(checkID(id));
                if (checkID(id)) {
    
    
                    if (!mapPeo.containsKey(id)) {
    
    
                        People curPeo = new People(name, id, status, j, getMinute(time));
                        mapPeo.put(id, curPeo);
                        curpeos.add(curPeo);
                        // 如果是合法记录并且身体状况是1的则加入error集合中
                        if (curPeo.status) {
    
    
                            String info = name+" "+id;
                            // System.out.println(info);
                            if (!error.contains(info))
                                error.add(info);
                        }
                    } else {
    
    
                        int last = mapPeo.get(id).day;
                        /*System.out.println("上一次领的时间:"+last);
                        System.out.println(last+p+1<=j );
                        System.out.println("最小间隔时间"+p);
                        System.out.println("相差时间"+(p+last+1));
                        System.out.println("今天是第几天"+ j);*/
                        if (last+p+1<= j || !mapPeo.get(id).get) {
    
    
                            mapPeo.get(id).day = i;
                            curpeos.add(mapPeo.get(id));
                            //System.out.println("加入成功");
                        }

                        // 如果是合法记录并且身体状况是1的则加入error集合中
                        if (status) {
    
    
                            String info = name+" "+id;
                            // System.out.println(info);
                            if (!error.contains(info))
                                error.add(info);
                        }
                    }

                }
            }
            Collections.sort(curpeos, new Comparator<People>() {
    
    
                @Override
                public int compare(People o1, People o2) {
    
    
                    return o1.time - o2.time;
                }
            });
            //System.out.println(curpeos.size());
            for (People peo : curpeos) {
    
    
                if (s == 0)
                    break;
                System.out.println(peo.name+" "+peo.id);
                peo.get = true;
                s--;
            }
        }
        //System.out.println("身体异常的有"+error.size());
        for (String info : error) {
    
    
            System.out.println(info);
        }
    }

    // 检查身份证是否合理
    public static boolean checkID(String id) {
    
    
        if (id.length() != 18) return false;
        for (char c : id.toCharArray()) {
    
    
            if (! Character.isDigit(c)) return false;
        }
        return true;
    }

    // 获取提交时的分钟数
    public static int getMinute(String time) {
    
    
        int hour = Integer.parseInt(time.substring(0,2));
        int minute = Integer.parseInt(time.substring(3));

        return hour*60+minute;
    }
}

L2-3 完全二叉树的层序遍历

一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是完美二叉树。对于深度为 D 的,有 N 个结点的二叉树,若其结点对应于相同深度完美二叉树的层序遍历的前 N 个结点,这样的树就是完全二叉树。

给定一棵完全二叉树的后序遍历,请你给出这棵树的层序遍历结果。

输入格式:
输入在第一行中给出正整数 N(≤30),即树中结点个数。第二行给出后序遍历序列,为 N 个不超过 100 的正整数。同一行中所有数字都以空格分隔。

输出格式:
在一行中输出该树的层序遍历序列。所有数字都以 1 个空格分隔,行首尾不得有多余空格。

输入样例:
8
91 71 2 34 10 15 55 18
输出样例:
18 34 55 71 2 10 15 91
#include<bits/stdc++.h>
using namespace std;

struct Node {
    
    
	int val;
	Node *l, *r;
};
Node *a[100];
int top = -1;
int v[100];

void dfs(Node *cur)
{
    
    
	if (cur == NULL)
		return;
	dfs(cur->l);
	dfs(cur->r);
	cur->val = v[++top];
}
int main()
{
    
    
	int n;
	cin >> n;
	
	for (int i = 0; i < n; i++)
	{
    
    
		cin >> v[i];
	}
	Node *root = new Node();
	a[++top] = root;
	int pre = 0;
	n--;
	Node *cur;
	while (n--)
	{
    
    
		cur = a[pre++];
		cur->l = new Node();
		a[++top] = cur->l;
		if (n)
		{
    
    
			cur->r = new Node();
			a[++top] = cur->r;
			n--;
		}
	}
	top = -1;
	dfs(root);
	top = 0;
	pre = 0;
	a[pre] = root;
    int i = 0;
	while (pre <= top)
	{
    
    
        if (i++>0)
            cout << " ";
		cur = a[pre++];
		cout << cur->val << "";
		if (cur->l)
			a[++top] = cur->l;
		if (cur->r)
			a[++top] = cur->r;
	}
	return 0;
}

L2-4 网红点打卡攻略

一个旅游景点,如果被带火了的话,就被称为“网红点”。大家来网红点游玩,俗称“打卡”。在各个网红点打卡的快(省)乐(钱)方法称为“攻略”。你的任务就是从一大堆攻略中,找出那个能在每个网红点打卡仅一次、并且路上花费最少的攻略。
在这里插入图片描述

输入样例:
6 13
0 5 2
6 2 2
6 0 1
3 4 2
1 5 2
2 5 1
3 1 1
4 1 2
1 6 1
6 3 2
1 2 1
4 5 3
2 0 2
7
6 5 1 4 3 6 2
6 5 2 1 6 3 4
8 6 2 1 6 3 4 5 2
3 2 1 5
6 6 1 3 4 5 2
7 6 2 1 3 4 5 2
6 5 2 1 4 3 6

输出样例:
3
5 11

样例说明:
第 2346 条都不满足攻略的基本要求,即不能做到从家里出发,在每个网红点打卡仅一次,且能回到家里。所以满足条件的攻略有 3 条。
第 1 条攻略的总路费是:(0->5) 2 + (5->1) 2 + (1->4) 2 + (4->3) 2 + (3->6) 2 + (6->2) 2 + (2->0) 2 = 14;
第 5 条攻略的总路费同理可算得:1 + 1 + 1 + 2 + 3 + 1 + 2 = 11,是一条更省钱的攻略;
第 7 条攻略的总路费同理可算得:2 + 1 + 1 + 2 + 2 + 2 + 1 = 11,与第 5 条花费相同,但序号较大,所以不输出。
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class L2_4 {
    
    
    public static void main(String[] args) {
    
    
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();  // 网红景点数
        int r = sc.nextInt();  // 存在道路数
        int[][] graph = new int[n+1][n+1];
        while (r-- > 0) {
    
    
            int a = sc.nextInt();
            int b = sc.nextInt();
            int c = sc.nextInt();
            graph[a][b] = c;
            graph[b][a] = c;
        }

        int k = sc.nextInt();  // 打卡攻略数
        Set<Integer> set = new HashSet<>();

        int count = 0; // 可以打卡所有景点的线路数
        int min = (int) (Math.pow(10, 9)+1);
        int index = -1;  // 记录第几条线路

        for (int i = 1; i <= k; i++) {
    
      
            set.clear();
            int m = sc.nextInt();  // 网红点数
            int cost = 0;  // 当前花费
            boolean flag = true;
            int start = 0, end = 0;
            for (int j = 0; j < m; j++) {
    
    
                end = sc.nextInt();
                if (graph[start][end] != 0) {
    
    
                    cost += graph[start][end];
                } else {
    
    
                    flag = false;
                    break;
                }
                set.add(end);
                start = end;
            }
            if (flag && graph[end][0] != 0 && set.size() == n && m==n) {
    
    
                cost += graph[end][0];
                if (cost < min) {
    
    
                    min = cost;
                    index = i;
                }
                count++;
            }
        }
        System.out.println(count);
        System.out.println(index+" "+min);
    }
}

加油!

感谢!

努力!

猜你喜欢

转载自blog.csdn.net/qq_46092061/article/details/123987142