L1-1 嫑废话上代码 (5分)
Linux 之父 Linus Torvalds 的名言是:“Talk is cheap. Show me the code.”(嫑废话,上代码)。本题就请你直接在屏幕上输出这句话。
输入格式:
本题没有输入。
输出格式:
在一行中输出 Talk is cheap. Show me the code.。
输入样例:
无
输出样例:
Talk is cheap. Show me the code.
#include<bits/stdc++.h>
using namespace std;
int main()
{
puts("Talk is cheap. Show me the code.");
}
L1-2 猫是液体 (5分)
测量一个人的体积是很难的,但猫就不一样了。因为猫是液体,所以可以很容易地通过测量一个长方体容器的容积来得到容器里猫的体积。本题就请你完成这个计算。
输入格式:
输入在第一行中给出 3 个不超过 100 的正整数,分别对应容器的长、宽、高。
输出格式:
在一行中输出猫的体积。
输入样例:
23 15 20
输出样例:
6900
#include <bits/stdc++.h>
using namespace std;
int main()
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
printf("%d\n", a * b * c);
}
L1-3 洛希极限 (10分)
科幻电影《流浪地球》中一个重要的情节是地球距离木星太近时,大气开始被木星吸走,而随着不断接近地木“刚体洛希极限”,地球面临被彻底撕碎的危险。但实际上,这个计算是错误的。
洛希极限(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
。
输入样例 1:
0.622 0 1.4
输出样例 1:
1.53 T_T
输入样例 2:
0.622 1 1.4
输出样例 2:
0.78 ^_^
#include <bits/stdc++.h>
using namespace std;
int main()
{
int b;
double a,c;
scanf("%lf%d%lf",&a,&b,&c);
if(!b)
{
double t = a * 2.455;
if(t<c)
printf("%.2lf ^_^\n",t);
else
printf("%.2lf T_T\n",t);
}
else
{
double t = a * 1.26;
if(t<c)
printf("%.2lf ^_^\n",t);
else
printf("%.2lf T_T\n",t);
}
return 0;
}
L1-4 调和平均 (10分)
N N N 个正数的算数平均是这些数的和除以 N N N,它们的调和平均是它们倒数的算数平均的倒数。本题就请你计算给定的一系列正数的调和平均值。
输入格式:
每个输入包含 1 1 1 个测试用例。每个测试用例第 1 1 1 行给出正整数 N ( ≤ 1000 ) N (≤1000) N(≤1000);第 2 2 2 行给出 N N N 个正数,都在区间 [ 0.1 , 100 ] [0.1,100] [0.1,100] 内。
输出格式:
在一行中输出给定数列的调和平均值,输出小数点后 2 2 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 n;
scanf("%d",&n);
double sum = 0.0;
for(int i=0;i<n;i++)
{
double x;
scanf("%lf",&x);
sum += 1.0/x;
}
sum /= 1.0*n;
sum = 1.0 / sum;
printf("%.2lf\n",sum);
return 0;
}
L1-5 胎压监测 (15分)
小轿车中有一个系统随时监测四个车轮的胎压,如果四轮胎压不是很平衡,则可能对行车造成严重的影响。
让我们把四个车轮 —— 左前轮、右前轮、右后轮、左后轮 —— 顺次编号为 1、2、3、4。本题就请你编写一个监测程序,随时监测四轮的胎压,并给出正确的报警信息。报警规则如下:
如果所有轮胎的压力值与它们中的最大值误差在一个给定阈值内,并且都不低于系统设定的最低报警胎压,则说明情况正常,不报警;
如果存在一个轮胎的压力值与它们中的最大值误差超过了阈值,或者低于系统设定的最低报警胎压,则不仅要报警,而且要给出可能漏气的轮胎的准确位置;
如果存在两个或两个以上轮胎的压力值与它们中的最大值误差超过了阈值,或者低于系统设定的最低报警胎压,则报警要求检查所有轮胎。
输入格式:
输入在一行中给出 6 6 6 个 [ 0 , 400 ] [0, 400] [0,400] 范围内的整数,依次为 1 ∼ 4 1 \sim 4 1∼4 号轮胎的胎压、最低报警胎压、以及胎压差的阈值。
输出格式:
根据输入的胎压值给出对应信息:
- 如果不用报警,输出
Normal
; - 如果有一个轮胎需要报警,输出
Warning: please check #X!
,其中X
是出问题的轮胎的编号; - 如果需要检查所有轮胎,输出
Warning: please check all the tires!
。
输入样例 1:
242 251 231 248 230 20
输出样例 1:
Normal
输入样例 2:
242 251 232 248 230 10
输出样例 2:
Warning: please check #3!
输入样例 3:
240 251 232 248 240 10
输出样例 3:
Warning: please check all the tires!
#include <bits/stdc++.h>
using namespace std;
int main()
{
int a[4], b, c;
scanf("%d%d%d%d%d%d", &a[0], &a[1], &a[2], &a[3], &b, &c);
int mx = max(max(a[0], a[1]), max(a[2], a[3]));
int n1 = 0, n2 = 0, id = -1;
for (int i = 0; i < 4; i++)
{
if (mx - a[i] > c)
n2++, id = i + 1;
if (a[i] < b)
n1++, id = i + 1;
}
if (n1 >= 2 || n2 >= 2)
puts("Warning: please check all the tires!");
else if (n1 == 1 || n2 == 1)
printf("Warning: please check #%d!\n", id);
else if (!n1 && !n2)
puts("Normal");
return 0;
}
L1-05 吃火锅 (15分)
以上图片来自微信朋友圈:这种天气你有什么破事打电话给我基本没用。但是如果你说“吃火锅”,那就厉害了,我们的故事就开始了。
本题要求你实现一个程序,自动检查你朋友给你发来的信息里有没有 chi1 huo3 guo1
。
输入格式:
输入每行给出一句不超过 80 80 80 个字符的、以回车结尾的朋友信息,信息为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。当读到某一行只有一个英文句点 . 时,输入结束,此行不算在朋友信息里。
输出格式:
首先在一行中输出朋友信息的总条数。然后对朋友的每一行信息,检查其中是否包含 chi1 huo3 guo1
,并且统计这样厉害的信息有多少条。在第二行中首先输出第一次出现 chi1 huo3 guo1
的信息是第几条(从 1 1 1 开始计数),然后输出这类信息的总条数,其间以一个空格分隔。题目保证输出的所有数字不超过 100 100 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
.
输出样例 1:
5
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
.
输出样例 2:
5
-_-#
#include <bits/stdc++.h>
using namespace std;
char s[10005];
int main()
{
int cnt = 0, ans = 0, id = 0;
while (cin.getline(s,10005))
{
if (strcmp(s, ".") == 0)
break;
cnt++;
if (strstr(s, "chi1 huo3 guo1"))
{
ans++;
if (!id)
id = cnt;
}
}
printf("%d\n", cnt);
if (!ans)
puts("-_-#");
else
printf("%d %d\n", id, ans);
return 0;
}
L1-6 前世档案 (20分)
网络世界中时常会遇到这类滑稽的算命小程序,实现原理很简单,随便设计几个问题,根据玩家对每个问题的回答选择一条判断树中的路径(如下图所示),结论就是路径终点对应的那个结点。
现在我们把结论从左到右顺序编号,编号从 1 开始。这里假设回答都是简单的“是”或“否”,又假设回答“是”对应向左的路径,回答“否”对应向右的路径。给定玩家的一系列回答,请你返回其得到的结论的编号。
输入格式:
输入第一行给出两个正整数: N ( ≤ 30 ) N(≤30) N(≤30)为玩家做一次测试要回答的问题数量; M ( ≤ 100 ) M(≤100) M(≤100)为玩家人数。
随后 M M M 行,每行顺次给出玩家的 N N N 个回答。这里用 y
代表“是”,用 n
代表“否”。
输出格式:
对每个玩家,在一行中输出其对应的结论的编号。
输入样例:
3 4
yny
nyy
nyn
yyn
输出样例:
3
5
6
2
#include <bits/stdc++.h>
using namespace std;
int n, m;
string s;
int find(int ans, int id)
{
if (id == n)
return ans;
if (s[n - id - 1] == 'y')
return find(ans, id + 1);
else
return find(ans + (1 << id), id + 1);
}
int main()
{
scanf("%d%d", &n, &m);
while (m--)
{
cin >> s;
cout << find(1, 0) << "\n";
}
return 0;
}
L1-7 刮刮彩票 (20分)
“刮刮彩票”是一款网络游戏里面的一个小游戏。如图所示:
每次游戏玩家会拿到一张彩票,上面会有 9 9 9 个数字,分别为数字 1 1 1 到数字 9 9 9,数字各不重复,并以 3 × 3 3×3 3×3 的“九宫格”形式排布在彩票上。
在游戏开始时能看见一个位置上的数字,其他位置上的数字均不可见。你可以选择三个位置的数字刮开,这样玩家就能看见四个位置上的数字了。最后玩家再从 3 3 3 横、 3 3 3 竖、 2 2 2 斜共 8 8 8 个方向中挑选一个方向,方向上三个数字的和可根据下列表格进行兑奖,获得对应数额的金币。
数字合计 | 获得金币 | 数字合计 | 获得金币 |
---|---|---|---|
6 | 10000 | 16 | 72 |
7 | 36 | 17 | 180 |
8 | 720 | 18 | 119 |
9 | 360 | 19 | 36 |
10 | 80 | 20 | 306 |
11 | 252 | 21 | 1080 |
12 | 108 | 22 | 144 |
13 | 72 | 23 | 1800 |
14 | 54 | 24 | 3600 |
15 | 180 |
现在请你写出一个模拟程序,模拟玩家的游戏过程。
输入格式:
输入第一部分给出一张合法的彩票,即用 3 3 3 行 3 3 3 列给出 0 0 0 至 9 9 9 的数字。$0 $表示的是这个位置上的数字初始时就能看见了,而不是彩票上的数字为 0 0 0。
第二部给出玩家刮开的三个位置,分为三行,每行按格式 x y
给出玩家刮开的位置的行号和列号(题目中定义左上角的位置为第 1 1 1 行、第 1 1 1 列。)。数据保证玩家不会重复刮开已刮开的数字。
最后一部分给出玩家选择的方向,即一个整数: 1 1 1 至 3 3 3 表示选择横向的第一行、第二行、第三行, 4 4 4 至 6 6 6表示纵向的第一列、第二列、第三列, 7 7 7、 8 8 8分别表示左上到右下的主对角线和右上到左下的副对角线。
输出格式:
对于每一个刮开的操作,在一行中输出玩家能看到的数字。最后对于选择的方向,在一行中输出玩家获得的金币数量。
输入样例:
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 mp[4][4];
int val[] = {
10000, 36, 720, 360, 80, 252, 108, 72, 54, 180, 72, 180, 119, 36, 306, 1080, 144, 1800, 3600};
int main()
{
int sum = 45;
int x1 = -1, y1 = -1;
for (int i = 1; i <= 3; i++)
for (int j = 1; j <= 3; j++)
{
scanf("%d", &mp[i][j]);
sum -= mp[i][j];
if (!mp[i][j])
x1 = i, y1 = j;
}
mp[x1][y1] = sum;
for (int i = 0; i < 3; i++)
{
int x, y;
scanf("%d%d", &x, &y);
printf("%d\n", mp[x][y]);
}
int op, ans = 0;
scanf("%d", &op);
for (int i = 1; i <= 3; i++)
{
if (op >= 1 && op <= 3)
ans += mp[op][i];
else if (op >= 4 && op <= 6)
ans += mp[i][op - 3];
else if (op == 7)
ans += mp[i][i];
else
ans += mp[i][4 - i];
}
printf("%d\n", val[ans - 6]);
return 0;
}
L2-8 简单计算器 (25分)
本题要求你为初学数据结构的小伙伴设计一款简单的利用堆栈执行的计算器。如上图所示,计算器由两个堆栈组成,一个堆栈 S 1 S_1 S1存放数字,另一个堆栈 S 2 S_2 S2存放运算符。计算器的最下方有一个等号键,每次按下这个键,计算器就执行以下操作:
- 从 S 1 S_1 S1中弹出两个数字,顺序为 n 1 n_1 n1和 n 2 n_2 n2;
- 从 S 2 S_2 S2中弹出一个运算符 o p op op;
- 执行计算 n 2 o p n 1 n_2 \ op \ n_1 n2 op n1;
- 将得到的结果压回 S 1 S_1 S1。
直到两个堆栈都为空时,计算结束,最后的结果将显示在屏幕上。
输入格式:
输入首先在第一行给出正整数 N ( 1 < N ≤ 1 0 3 ) N(1<N≤10^3) N(1<N≤103),为 S 1 S_1 S1中数字的个数。
第二行给出 N N N 个绝对值不超过 100 100 100 的整数;第三行给出 N − 1 N−1 N−1 个运算符 —— 这里仅考虑 +
、-
、*
、/
这四种运算。一行中的数字和符号都以空格分隔。
输出格式:
将输入的数字和运算符按给定顺序分别压入堆栈 S 1 S_1 S1和 S 2 S_2 S2 ,将执行计算的最后结果输出。注意所有的计算都只取结果的整数部分。题目保证计算的中间和最后结果的绝对值都不超过 1 0 9 10^9 109 。
如果执行除法时出现分母为零的非法操作,则在一行中输出:ERROR: X/0
,其中 X
是当时的分子。然后结束程序。
输入样例 1:
5
40 5 8 3 2
/ * - +
输出样例 1:
2
输入样例 2:
5
2 5 8 4 4
- / - +
输出样例 2:
ERROR: 5/0
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
stack<int> s1;
stack<char> s2;
for (int i = 0, x; i < n; i++)
cin >> x, s1.push(x);
for (int i = 1; i < n; i++)
{
char x;
cin >> x;
s2.push(x);
}
int flag = 1;
while (!s2.empty())
{
int n1 = s1.top();
s1.pop();
int n2 = s1.top();
s1.pop();
char x = s2.top();
s2.pop();
if (x == '+')
s1.push(n1 + n2);
else if (x == '-')
s1.push(n2 - n1);
else if (x == '*')
s1.push(n1 * n2);
else
{
if (n1 == 0)
{
flag = 0;
printf("ERROR: %d/0\n", n2);
break;
}
else
s1.push(n2 / n1);
}
}
if (flag)
printf("%d\n", s1.top());
return 0;
}
L2-9 口罩发放 (25分)
为了抗击来势汹汹的 COVID19 新型冠状病毒,全国各地均启动了各项措施控制疫情发展,其中一个重要的环节是口罩的发放。
某市出于给市民发放口罩的需要,推出了一款小程序让市民填写信息,方便工作的开展。小程序收集了各种信息,包括市民的姓名、身份证、身体情况、提交时间等,但因为数据量太大,需要根据一定规则进行筛选和处理,请你编写程序,按照给定规则输出口罩的寄送名单。
输入格式:
输入第一行是两个正整数 D D D 和 P ( 1 ≤ D , P ≤ 30 ) P(1≤D,P≤30) P(1≤D,P≤30),表示有 D D D 天的数据,市民两次获得口罩的时间至少需要间隔 P P P 天。
接下来 D D D 块数据,每块给出一天的申请信息。第 i i i 块数据 ( i = 1 , ⋯ , D ) (i=1,⋯,D) (i=1,⋯,D)的第一行是两个整数 T i T_i Ti和 S i ( 1 ≤ T i , S i ≤ 1000 S_i(1≤T_{i}, S_{i} ≤1000 Si(1≤Ti,Si≤1000,表示在第 i i i天有 T i T_i Ti条申请,总共有 S i S_i Si个口罩发放名额。随后 T i T_{i} Ti行,每行给出一条申请信息,格式如下:
姓名 身份证号 身体情况 提交时间
给定数据约束如下:
姓名
是一个长度不超过 10 10 10 的不包含空格的非空字符串;身份证号
是一个长度不超过 20 20 20 的非空字符串;身体情况
是 0 0 0 或者 1 1 1, 0 0 0 表示自觉良好, 1 1 1 表示有相关症状;提交时间
是 hh:mm,为 24 24 24小时时间(由00:00
到23:59
。例如 09:08。)。注意,给定的记录的提交时间不一定有序;身份证号
各不相同,同一个身份证号被认为是同一个人,数据保证同一个身份证号姓名是相同的。
能发放口罩的记录要求如下:
- 身份证号 必须是 18 18 18 位的数字(可以包含前导0);
- 同一个身份证号若在第 i i i 天申请成功,则接下来的 P P P 天不能再次申请。也就是说,若第 i i i 天申请成功,则等到第 i + P + 1 i+P+1 i+P+1天才能再次申请;
- 在上面两条都符合的情况下,按照提交时间的先后顺序发放,直至全部记录处理完毕或 S i S_i Si个名额用完。如果提交时间相同,则按照在列表中出现的先后顺序决定。
输出格式:
对于每一天的申请记录,每行输出一位得到口罩的人的姓名及身份证号,用一个空格隔开。顺序按照发放顺序确定。
在输出完发放记录后,你还需要输出有合法记录的、身体状况为 1 1 1 的申请人的姓名及身份证号,用空格隔开。顺序按照申请记录中出现的顺序确定,同一个人只需要输出一次。
输入样例:
4 2
5 3
A 123456789012345670 1 13:58
B 123456789012345671 0 13:58
C 12345678901234567 0 13:22
D 123456789012345672 0 03:24
C 123456789012345673 0 13:59
4 3
A 123456789012345670 1 13:58
E 123456789012345674 0 13:59
C 123456789012345673 0 13:59
F F 0 14:00
1 3
E 123456789012345674 1 13:58
1 1
A 123456789012345670 0 14:11
输出样例:
D 123456789012345672
A 123456789012345670
B 123456789012345671
E 123456789012345674
C 123456789012345673
A 123456789012345670
A 123456789012345670
E 123456789012345674
样例解释:
输出中,第一行到第三行是第一天的部分;第四、五行是第二天的部分;第三天没有符合要求的市民;第六行是第四天的部分。最后两行按照出现顺序输出了可能存在身体不适的人员。
#include <bits/stdc++.h>
using namespace std;
map<string, int> mp;
set<string> st;
vector<pair<string, string>> ans;
struct Node
{
string name, id;
int time, idx;
Node() {
}
Node(string n0, string i0, int t0, int i1) : name(n0), id(i0), time(t0), idx(i1) {
}
bool operator<(const Node &C) const
{
if (time == C.time)
return idx < C.idx;
return time < C.time;
}
} node[1005];
int main()
{
int d, p;
scanf("%d%d", &d, &p);
for (int _ = 1; _ <= d; _++)
{
int t, s, cnt = 0;
scanf("%d%d", &t, &s);
for (int i = 1; i <= t; i++)
{
string name, id;
int state, h, m;
cin >> name >> id >> state;
scanf("%d:%d", &h, &m);
int flag = 1;
for (int j = 0; j < id.size(); j++)
if (!isdigit(id[j]))
{
flag = 0;
break;
}
if (id.length() == 18 && flag)
{
node[cnt++] = {
name, id, h * 60 + m, i};
if (state == 1 && st.count(id) == 0)
{
ans.push_back({
name, id});
st.insert(id);
}
}
}
sort(node, node + cnt);
for (int i = 0, j = 0; i < cnt && j < s; i++)
{
if (!mp[node[i].id] || _ - mp[node[i].id] > p)
{
cout << node[i].name << " " << node[i].id << "\n";
j++;
mp[node[i].id] = _;
}
}
}
for (auto i : ans)
cout << i.first << " " << i.second << "\n";
return 0;
}
L2-10 完全二叉树的层序遍历 (25分)
一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是完美二叉树。对于深度为 D D D 的,有 N N N 个结点的二叉树,若其结点对应于相同深度完美二叉树的层序遍历的前 N N N 个结点,这样的树就是完全二叉树。
给定一棵完全二叉树的后序遍历,请你给出这棵树的层序遍历结果。
输入格式:
输入在第一行中给出正整数 N ( ≤ 30 ) N(≤30) N(≤30),即树中结点个数。第二行给出后序遍历序列,为 N N N 个不超过 100 100 100 的正整数。同一行中所有数字都以空格分隔。
输出格式:
在一行中输出该树的层序遍历序列。所有数字都以 1 1 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;
int a[105], ans[105], n, cnt = 1;
void build(int rt)
{
if (rt > n)
return;
build(rt << 1);
build(rt << 1 | 1);
ans[rt] = a[cnt++];
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
build(1);
for (int i = 1; i <= n; i++)
printf("%d%c", ans[i], (i == n) ? '\n' : ' ');
return 0;
}
L2-11 网红点打卡攻略 (25分)
一个旅游景点,如果被带火了的话,就被称为“网红点”。大家来网红点游玩,俗称“打卡”。在各个网红点打卡的快(省)乐(钱)方法称为“攻略”。你的任务就是从一大堆攻略中,找出那个能在每个网红点打卡仅一次、并且路上花费最少的攻略。
输入格式:
首先第一行给出两个正整数:网红点的个数 N ( 1 < N ≤ 200 ) N(1<N≤200) N(1<N≤200)和网红点之间通路的条数 M M M。随后 M M M 行,每行给出有通路的两个网红点、以及这条路上的旅行花费(为正整数),格式为“网红点 1 1 1 网红点2 费用”,其中网红点从 1 1 1 到 N N N 编号;同时也给出你家到某些网红点的花费,格式相同,其中你家的编号固定为 0
。
再下一行给出一个正整数 K K K,是待检验的攻略的数量。随后 K K K 行,每行给出一条待检攻略,格式为:
n V 1 V 2 ⋯ V n n \ V_{1} \ V_{2}\ ⋯ \ V_{n} n V1 V2 ⋯ Vn
其中 n ( ≤ 200 ) n(≤200) n(≤200) 是攻略中的网红点数, V i V_i Vi是路径上的网红点编号。这里假设你从家里出发,从 V 1 V_1 V1开始打卡,最后从 V n V_n Vn回家。
输出格式:
在第一行输出满足要求的攻略的个数。
在第二行中,首先输出那个能在每个网红点打卡仅一次、并且路上花费最少的攻略的序号(从 1 1 1 开始),然后输出这个攻略的总路费,其间以一个空格分隔。如果这样的攻略不唯一,则输出序号最小的那个。
题目保证至少存在一个有效攻略,并且总路费不超过 1 0 9 10^9 109 。
输入样例:
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
样例说明:
第 2、3、4、6 条都不满足攻略的基本要求,即不能做到从家里出发,在每个网红点打卡仅一次,且能回到家里。所以满足条件的攻略有 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 条花费相同,但序号较大,所以不输出。
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int mp[1005][1005], a[1005];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++)
{
if (i == j)
mp[i][j] = 0;
else
mp[i][j] = INF;
}
for (int i = 0; i < m; i++)
{
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
mp[a][b] = mp[b][a] = w;
}
int k;
scanf("%d", &k);
int cnt = 0, ans = INF, id = -1;
for (int _ = 1; _ <= k; _++)
{
set<int> vis;
int num;
scanf("%d", &num);
for (int i = 0; i < num; i++)
scanf("%d", &a[i]);
a[num] = 0;
if (num < n)
continue;
int last = 0, sum = 0, flag = 1;
for (int i = 0; i <= num; i++)
{
int n1 = vis.size();
vis.insert(a[i]);
if (n1 == vis.size() || mp[last][a[i]] == INF)
{
flag = 0;
break;
}
sum += mp[last][a[i]];
last = a[i];
}
if (flag && vis.size() == n + 1)
{
cnt++;
if (sum < ans)
{
ans = sum;
id = _;
}
}
}
printf("%d\n", cnt);
printf("%d %d\n", id, ans);
return 0;
}
L3-12 那就别担心了 (30分)
下图转自“英式没品笑话百科”的新浪微博 —— 所以无论有没有遇到难题,其实都不用担心。
博主将这种逻辑推演称为“逻辑自洽”,即从某个命题出发的所有推理路径都会将结论引导到同一个最终命题(开玩笑的,千万别以为这是真正的逻辑自洽的定义……)。现给定一个更为复杂的逻辑推理图,本题就请你检查从一个给定命题到另一个命题的推理是否是“逻辑自洽”的,以及存在多少种不同的推理路径。例如上图,从“你遇到难题了吗?”到“那就别担心了”就是一种“逻辑自洽”的推理,一共有 3 条不同的推理路径。
输入格式:
输入首先在一行中给出两个正整数 N ( 1 < N ≤ 500 ) N(1<N≤500) N(1<N≤500)和 M M M,分别为命题个数和推理个数。这里我们假设命题从 1 1 1 到 N N N 编号。
接下来 M M M 行,每行给出一对命题之间的推理关系,即两个命题的编号 S1 S2
,表示可以从 S1
推出 S2
。题目保证任意两命题之间只存在最多一种推理关系,且任一命题不能循环自证(即从该命题出发推出该命题自己)。
最后一行给出待检验的两个命题的编号 A B
。
输出格式:
在一行中首先输出从 A
到B
有多少种不同的推理路径,然后输出 Yes
如果推理是“逻辑自洽”的,或 No
如果不是。
题目保证输出数据不超过 10 9 10^9 109。
输入样例 1:
7 8
7 6
7 4
6 5
4 1
5 2
5 3
2 1
3 1
7 1
输出样例 1:
3 Yes
输入样例 2:
7 8
7 6
7 4
6 5
4 1
5 2
5 3
6 1
3 1
7 1
输出样例 2:
3 No
#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
const int maxm = 5e5 + 5;
struct edge
{
int to, nxt;
} E[maxm];
int head[maxn], dp[maxn], cnt, st, ed, flag;
void addedge(int u, int v)
{
E[cnt].to = v;
E[cnt].nxt = head[u];
head[u] = cnt++;
}
void init()
{
memset(head, -1, sizeof(head));
memset(dp, -1, sizeof(dp));
cnt = 0;
flag = 1;
}
int dfs(int u)
{
if (dp[u] != -1)
return dp[u];
int sum = 0;
for (int i = head[u]; ~i; i = E[i].nxt)
{
int v = E[i].to;
sum += dfs(v);
}
if (sum == 0)
flag = 0;
return dp[u] = sum;
}
int main()
{
init();
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i++)
{
int x, y;
scanf("%d%d", &x, &y);
addedge(x, y);
}
scanf("%d%d", &st, &ed);
dp[ed] = 1;
printf("%d ", dfs(st));
puts(flag ? "Yes" : "No");
return 0;
}