2018年全国多校算法寒假训练营练习比赛(第一场)
题目链接:https://www.nowcoder.com/acm/contest/67#question
由于被J题题意卡了一下,又被F题卡了一下,主要是自己题目没有读清楚,因此成绩不是很理想。但是该补的还是要补的。
官方题解
2018年全国多校算法寒假训练营练习比赛(第一场)
题解
A 贪心+枚举
同类型配件只取最高的,最后枚举每一把枪把配件装好后,取威力最大的输出
B 栈
对于速度递增的牌,作为连锁发动。
如果后发动的一张牌比前一张牌速度要高,将它压入栈。否则,先将栈内的牌按效果发动。
如果是让前一张牌失效的,直接pop一张牌即可。如果是连锁中的牌失效的牌,将栈清空。
C 模拟
注意吃子例和不被吃子例,然后模拟即可。
D 找规律-三进制
观察汉诺塔变形的每个圆盘的移动规律,1号盘移动两次,接着2号盘会移动一次,二号盘移动两次后,3号盘会移动一次……如果将1号盘作为三进制数的最低位,2号盘作为第二位。那么移动规律就是三进制的计数规律。
第一行为三进制计数
第二行为要移动的盘
000->001->002->010->011->012->020->021->022->100……
1号 1号 2号 1号 1号 2号 1号 1号 3号
那么按照规律,将k转化为3进制数后,计算出每个盘的移动次数,就可以判断出该盘在哪个塔座上。
E 搜索
建立图后,进行搜索,找到最小值便可。
需要注意的是,卡片可以重复使用。
F 模拟
模拟跑毒过程。从样例可以看出,人物先行动,然后判断人物是否在安全区,若不在安全区内再扣血。
所以对于很多同学的疑问:进入安全区后人物血量刚好小于等于0,是否能安全进安全区。答案是,安全的。
G 分形
没什么好说的……网上有类似题的源码
H 递推
就是一个斐波那契数列
I 模拟
暴力跑0~1000内的数,判断是否符合条件即可
J 找规律
规律是,奇数层会得到一个0
偶数层会得到3个0
每i+2个数i会合并为一个i+1
两个0合并为一个1
三个1合并为一个2
……
非官方题解
A 大吉大利,今晚吃鸡——枪械篇
我认为官方题解的解释已经很清楚了,所以直接放个代码吧
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class Main {
static final int maxm = 1010;
static double[] ms;
static double[] p;
static List<Integer>[] canUse = new List[maxm];
public static void main(String[] args) {
FastScanner fs = new FastScanner();
while(fs.hasNext()) {
ms = new double[maxm];
p = new double[maxm];
int n = fs.nextInt();
int m = fs.nextInt();
for(int i = 1; i <= n; i++) {
p[i] = fs.nextInt();
canUse[i] = new ArrayList<>();
int k = fs.nextInt();
for(int j = 0; j < k; j++) {
int t = fs.nextInt();
canUse[i].add(t);
}
}
for(int i = 0; i < m; i++) {
int index = fs.nextInt();
double t = fs.nextDoube();
if(t > ms[index]) {
ms[index] = t;
}
}
double max = -1;
for(int i = 1; i <= n; i++) {
double temp = p[i];
double sum = 1;
for(int t : canUse[i]) {
sum += ms[t];
}
temp *= sum;
if(temp > max) {
max = temp;
}
}
System.out.printf("%.4f\n", max);
}
}
public static class FastScanner {
private BufferedReader br;
private StringTokenizer st;
// 级别最高
void eat(String s) {
st = new StringTokenizer(s);
}
// 级别第二
public FastScanner() {
br = new BufferedReader(new InputStreamReader(System.in));
eat("");
}
public FastScanner(String s) {
try {
br = new BufferedReader(new FileReader(new File(s)));
eat("");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
// 级别第三
public String nextLine() {
try {
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public boolean hasNext() {
while(!st.hasMoreTokens()) {
String s = nextLine();
if(s == null) {
return false;
}
eat(s);
}
return true;
}
// 级别第四
public String nextToken() {
hasNext();
return st.nextToken();
}
// 级别第五
public int nextInt() {
return Integer.valueOf(nextToken());
}
public long nextLong() {
return Long.valueOf(nextToken());
}
public double nextDoube() {
return Double.valueOf(nextToken());
}
}
}
B 最强的决斗者一切都是必然的!
好像官方题解也说的很清楚,直接放个代码
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;
import java.util.StringTokenizer;
public class Main {
static Stack<Integer> stack;
static Stack<Integer> sx;
public static void main(String[] args) {
FastScanner fs = new FastScanner();
while (fs.hasNext()) {
int n = fs.nextInt();
long sum = 0;
int last = -1;
stack = new Stack<>();
sx = new Stack<>();
for(int i = 0; i < n; i++) {
int s = fs.nextInt();
int t = fs.nextInt();
if(s >= last) {
stack.push(t);
if(t <= 2) {
int k = fs.nextInt();
sx.push(k);
}
} else {
sum += getSolution();
if(t <= 2) {
int k = fs.nextInt();
sx.push(k);
}
stack.push(t);
}
last = s;
}
sum += getSolution();
System.out.println(sum);
}
}
public static long getSolution() {
long res = 0;
while (!stack.isEmpty()) {
int t = stack.pop();
if(t == 1) {
res += sx.pop();
} else if(t == 2) {
res += (stack.size() + 1) * sx.pop();
} else if(t == 3) {
stack.clear();
sx.clear();
} else if(t == 4) {
if(stack.size() > 0) {
int t1 = stack.pop();
if(t1 <= 2) {
sx.pop();
}
}
}
}
return res;
}
public static class FastScanner {
private BufferedReader br;
private StringTokenizer st;
// 级别最高
void eat(String s) {
st = new StringTokenizer(s);
}
// 级别第二
public FastScanner() {
br = new BufferedReader(new InputStreamReader(System.in));
eat("");
}
public FastScanner(String s) {
try {
br = new BufferedReader(new FileReader(new File(s)));
eat("");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
// 级别第三
public String nextLine() {
try {
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public boolean hasNext() {
while(!st.hasMoreTokens()) {
String s = nextLine();
if(s == null) {
return false;
}
eat(s);
}
return true;
}
// 级别第四
public String nextToken() {
hasNext();
return st.nextToken();
}
// 级别第五
public int nextInt() {
return Integer.valueOf(nextToken());
}
public long nextLong() {
return Long.valueOf(nextToken());
}
public double nextDoube() {
return Double.valueOf(nextToken());
}
}
}
C 六子冲
这是个大模拟,我写不动。
然后在网上找了一份题解简单看了一下:http://blog.csdn.net/zhao5502169/article/details/79131838
D N阶汉诺塔变形
这个规则和原来的汉诺塔规则是不一样的,正如Claris老师所说,要先看清规则再写题。。。
然后我找出了规律之后,还是没有能写出来,因为没有想到可以转成这样:
这样的话就可以:
然后我们发现如果把所有大于1的标号看成1,那么k步1出现的次数是 k/1
如果把所有大于2的标号看成2,那么k步2出现的次数是 k/3
如果把所有大于3的标号看成3,那么k步3出现的次数是 k/3^2
如果把所有大于4的标号看成4,那么k步4出现的次数是 k/3^3
因为这样我们就可以把移动的循环看成6步,刚才说过每出现两次i,出现一下i+1,所以每移动两步停一下(如图③和⑥过程),所以k/3^(i-1)可以表示通过这个循环的步数,取余6就知道i走到哪里了
原题解链接:http://www.bubuko.com/infodetail-2471409.html
只能说自己还是菜啊,想不出来
E 恋与程序员
哎这个题目好简单啊,比赛的时候我怎么没有看。。。我从网上找了一份代码改了改,因为是会做的。。。
我是从这个网站上改的:https://www.nowcoder.com/acm/contest/view-submission?submissionId=21094442
#include <stdio.h>
#include <string.h>
int ans;
int n, m, k, c;
const int maxn = 110;
int cost[maxn];
int graphes[maxn][maxn];
int visited[maxn];
const int inf = 0x3f3f3f3f;
void dfs(int s, int d) {
if(visited[s]) {
return;
}
visited[s] = 1;
if (s == c) {
ans = ans < d ? ans : d;
visited[s] = 0;
return;
}
for(int i = 1;i <= n; i++) {
if (graphes[s][i]) {
int t = cost[graphes[s][i]];
cost[graphes[s][i]] = 0;
dfs(i, d+t);
cost[graphes[s][i]] = t;
}
}
visited[s] = 0;
}
int main() {
while (~scanf("%d%d%d%d", &n, &m, &k, &c)) {
memset(cost, 0, sizeof(cost));
memset(graphes, 0, sizeof(graphes));
memset(visited, 0, sizeof(visited));
ans = inf;
int i;
for (i = 0; i < m; i++) {
int u, v, e;
scanf("%d%d%d", &u, &v, &e);
graphes[u][v] = e;
}
for (i = 0; i < k; i++) {
int a, b;
scanf("%d%d", &a, &b);
cost[a] = b;
}
dfs(1, 0);
printf("%d\n", ans);
}
}
好吧,其实还是学到了一点东西的,就是关于建图的时候直接用数组来建图,而不是想着别的什么乱七八糟的数据结构。
F 大吉大利,今晚吃鸡——跑毒篇
详细解释见注释
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) {
FastScanner fs = new FastScanner();
int T = fs.nextInt();
while (T-->0) {
int a = fs.nextInt();
int b = fs.nextInt();
int c = fs.nextInt();
getSolution(a, b, c);
}
}
public static void getSolution(int a, int b, int c) {
if(solve(100, b, c, a)) {
System.out.println("YES");
} else {
System.out.println("NO");
}
}
public static boolean solve(int blood, int distance, int packs, int a) {
if(blood >= 100) {
blood = 100;
}
// 这个条件需要仔细研究样例的说明,其中说到,链接:
/*https://www.nowcoder.com/acm/contest/67/F
来源:牛客网
42s的时候,角色跑了30m,血量剩余2%。当43s的时候,角色跑了31m进入了安全区内,不再扣血。*/
// 这说明是先行动后扣血
if(blood <= 0 && distance <= 0) {
return true;
}
if(blood <= 0) {
return false;
}
if(distance <= 0) {
return true;
}
if(blood > distance * a) {
return true;
}
// 如果没有包或者舔包一定死
if(packs <= 0 || a * 6 >= blood) {
return solve(blood - a, distance - 1, packs, a);
}
// 走一步或者舔包,注意舔包是恢复到80%,不是增加80%
return solve(blood - a, distance - 1, packs, a) || solve(80, distance, packs - 1, a);
}
public static class FastScanner {
private BufferedReader br;
private StringTokenizer st;
// 级别最高
void eat(String s) {
st = new StringTokenizer(s);
}
// 级别第二
public FastScanner() {
br = new BufferedReader(new InputStreamReader(System.in));
eat("");
}
public FastScanner(String s) {
try {
br = new BufferedReader(new FileReader(new File(s)));
eat("");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
// 级别第三
public String nextLine() {
try {
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public boolean hasNext() {
while(!st.hasMoreTokens()) {
String s = nextLine();
if(s == null) {
return false;
}
eat(s);
}
return true;
}
// 级别第四
public String nextToken() {
hasNext();
return st.nextToken();
}
// 级别第五
public int nextInt() {
return Integer.valueOf(nextToken());
}
public long nextLong() {
return Long.valueOf(nextToken());
}
public double nextDoube() {
return Double.valueOf(nextToken());
}
}
}
G 圆圈
圆n:
空格 圆n-1
圆n-1 空格 圆n-1
空格 圆n-1
这题不用Java是因为Java的输出真的太慢了,到了后面根本跑不完
#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;
int size[10];
const int maxn = 2200;
char a[maxn][maxn];
void show(int n, int sx, int sy) {
if(n == 0) {
a[sx][sy] = 'O';
return;
}
show(n-1, sx, sy + size[n]);
show(n-1, sx + size[n], sy);
show(n-1, sx + size[n] * 2, sy + size[n]);
show(n-1, sx + size[n], sy + size[n] * 2);
}
void print(int n) {
if( n == 0 ) {
printf("O\n");
return;
}
for(int i = 0 ; i < size[n] * 3 ;i++) {
int t = size[n] * 3;
while (a[i][t] != 'O'){
t--;
}
for(int j = 0; j <= t; j++) {
if (a[i][j] == 'O') {
printf("O");
} else {
printf(" ");
}
}
printf("\n");
}
}
int main() {
size[0] = 0;
size[1] = 1;
for (int i = 2 ;i < 10 ;i++ ) {
size[i] = size[i-1] * 3;
}
int T, n;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
memset(a, 0, sizeof(a));
show(n, 0, 0);
print(n);
}
return 0;
}
H 方块与收纳盒
读题便能发现是斐波那契数列
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
static long[] s = new long[100];
public static long getSoultion(int x) {
if(s[x] != 0) {
return s[x];
}
long res = getSoultion(x - 1) + getSoultion(x - 2);
s[x] = res;
return res;
}
public static void main(String[] args) {
s[1] = 1;
s[2] = 2;
FastScanner fs = new FastScanner();
int T = fs.nextInt();
for(int i = 0; i < T; i++) {
int x = fs.nextInt();
System.out.println(getSoultion(x));
}
}
public static class FastScanner {
private BufferedReader br;
private StringTokenizer st;
// 级别最高
void eat(String s) {
st = new StringTokenizer(s);
}
// 级别第二
public FastScanner() {
br = new BufferedReader(new InputStreamReader(System.in));
eat("");
}
public FastScanner(String s) {
try {
br = new BufferedReader(new FileReader(new File(s)));
eat("");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
// 级别第三
public String nextLine() {
try {
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public boolean hasNext() {
while(!st.hasMoreTokens()) {
String s = nextLine();
if(s == null) {
return false;
}
eat(s);
}
return true;
}
// 级别第四
public String nextToken() {
hasNext();
return st.nextToken();
}
// 级别第五
public int nextInt() {
return Integer.valueOf(nextToken());
}
public long nextLong() {
return Long.valueOf(nextToken());
}
public double nextDoube() {
return Double.valueOf(nextToken());
}
}
}
I 找数字个数
这个直接暴力枚举即可
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) {
FastScanner fs = new FastScanner();
int T = fs.nextInt();
while (T-- > 0) {
int a = fs.nextInt();
int b = fs.nextInt();
int cnt = 0;
for (int i = 1; i <= 1000; i++) {
boolean like = true;
if (i % a == 0) {
like = false;
}
if (i % b == 0) {
like = false;
}
String t = String.valueOf(i);
String s1 = String.valueOf(a);
for (int j = 0; j < s1.length(); j++) {
if (t.contains(String.valueOf(s1.charAt(j)))) {
like = false;
break;
}
}
String s2 = String.valueOf(b);
for (int j = 0; j < s2.length(); j++) {
if (t.contains(String.valueOf(s2.charAt(j)))) {
like = false;
break;
}
}
if (like) {
cnt++;
}
}
System.out.println(cnt);
}
}
public static class FastScanner {
private BufferedReader br;
private StringTokenizer st;
// 级别最高
void eat(String s) {
st = new StringTokenizer(s);
}
// 级别第二
public FastScanner() {
br = new BufferedReader(new InputStreamReader(System.in));
eat("");
}
public FastScanner(String s) {
try {
br = new BufferedReader(new FileReader(new File(s)));
eat("");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
// 级别第三
public String nextLine() {
try {
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public boolean hasNext() {
while (!st.hasMoreTokens()) {
String s = nextLine();
if (s == null) {
return false;
}
eat(s);
}
return true;
}
// 级别第四
public String nextToken() {
hasNext();
return st.nextToken();
}
// 级别第五
public int nextInt() {
return Integer.valueOf(nextToken());
}
public long nextLong() {
return Long.valueOf(nextToken());
}
public double nextDoube() {
return Double.valueOf(nextToken());
}
}
}
J 闯关的lulu
这条题目题意我被卡了一下,直接先跳过去做跑毒了。
这题目通过观察可以得出官方题解的结论,但是我当时读题目的时候有个问题,就是说,如果9超过10个了,之后加上去的数字是算10,还是算1个1和1个0呢?后来我看了别人代码知道,原来是算10。。。另外当时自己的代码也写挫了。。。赛后补题。
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
// TODO
public class Main {
static String string;
static int[] cnt;
public static void main(String[] args) {
FastScanner fs = new FastScanner();
int T = fs.nextInt();
while (T-- > 0) {
cnt = new int[10000];
int n = fs.nextInt();
change(n);
System.out.println(gen());
}
}
public static String gen() {
StringBuilder sb = new StringBuilder();
for (int i = cnt.length - 1; i >= 0; i--) {
for (int j = 0; j < cnt[i]; j++) {
sb.append(i);
}
}
return sb.toString();
}
public static void change(int n) {
int total = n / 2 * 4;
if(n % 2 == 1) {
total += 1;
}
cnt[0] = total;
for(int i = 0; i < cnt.length - 1; i++) {
cnt[i+1] += cnt[i] / (i+2);
cnt[i] %= (i + 2);
}
}
public static class FastScanner {
private BufferedReader br;
private StringTokenizer st;
// 级别最高
void eat(String s) {
st = new StringTokenizer(s);
}
// 级别第二
public FastScanner() {
br = new BufferedReader(new InputStreamReader(System.in));
eat("");
}
public FastScanner(String s) {
try {
br = new BufferedReader(new FileReader(new File(s)));
eat("");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
// 级别第三
public String nextLine() {
try {
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public boolean hasNext() {
while (!st.hasMoreTokens()) {
String s = nextLine();
if (s == null) {
return false;
}
eat(s);
}
return true;
}
// 级别第四
public String nextToken() {
hasNext();
return st.nextToken();
}
// 级别第五
public int nextInt() {
return Integer.valueOf(nextToken());
}
public long nextLong() {
return Long.valueOf(nextToken());
}
public double nextDoube() {
return Double.valueOf(nextToken());
}
}
}
总结
自己写水题的经验还不够,当场写的时候总是有小Bug,比如说忘记输出换行符。另外读题目需要读清楚,比如跑毒那题。**C题是个大模拟,我觉得我有时间需要认真写一下这题,锻炼一下细节。**D题的思维还是要学习一下。E题学到了用数组建图的方法,当然主要还是因为自己图论没怎么写过,所以不是很熟。然后自己学到了分形的一种做法,很开心。