1、玩具
排序后以距离2进行遍历,只加更贵的。
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
有 n个玩具,第 i 个玩具的价格是 ai 元,超市里搞促销活动,购买 2 个玩具即可免单其中价格较低的一个,价格相等也免单其中一个。牛牛想买下所有玩具,至少需要花多少元?
输入描述:
第一行一个正整数 n(1≤n≤106)。
第二行 n个正整数,第 i 个表示 ai(1≤ai≤109)。
输出描述:
输出一行一个正整数,表示答案。
示例1
输入
3 1 2 3
输出
4
说明
第二个和第三个一起买,花 3 元,再花 1 元买下第一个,合计 4 元。
import java.util.Scanner;
import java.util.Arrays;
public class Main{
static long values = 0L;
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int []value = new int[n];
for(int i=0;i<n;i++){
value[i]=sc.nextInt();
}
Arrays.sort(value);//排序
for(int i=n-1;i>=0;i-=2){//因为是购买2个从中舍去比较小的,那么我们只需要排序后将从大到小每次占据首位的加上即可
values+=value[i];
}
System.out.println(values);
}
}
2、滑雪
dp+dfs
思路看注释
描述
给定一个n×m 的矩阵,矩阵中的数字表示滑雪场各个区域的高度,你可以选择从任意一个区域出发,并滑向任意一个周边的高度严格更低的区域(周边的定义是上下左右相邻的区域)。请问整个滑雪场中最长的滑道有多长?(滑道的定义是从一个点出发的一条高度递减的路线)。
(本题和矩阵最长递增路径类似,该题是当年NOIP的一道经典题)
数据范围:1≤n,m≤100 ,矩阵中的数字满足 1≤val≤1000
输入描述:
第一行输入两个正整数 n 和 m 表示矩阵的长宽。
后续 n 行输入中每行有 m 个正整数,表示矩阵的各个元素大小。
输出描述:
输出最长递减路线。
示例1
输入:
5 5 1 2 3 4 5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 10 9
输出:
25
说明:
从25出发,每次滑向周边比当前小 1 的区域。 25->24->23->22->......->1
import java.util.*;
import java.io.*;
//动态规划+dfs
public class Main {
static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(System.out)));
static int[]dx = new int[] {0, 1, -1, 0}; //定义状态
static int[]dy = new int[] {1, 0, 0, -1};
static int n = 0;
static int m = 0;
public static void main(String[] args) throws IOException {
String[]s = in.readLine().split(" ");
//定义滑雪场矩阵行列
n = Integer.parseInt(s[0]);
m = Integer.parseInt(s[1]);
int[][] map = new int[n][m];
//给滑雪场赋值
for (int i = 0; i < n; i++) {
s = in.readLine().split(" ");
for (int j = 0; j < m; j++) {
map[i][j] = Integer.parseInt(s[j]);
}
}
//通过遍历滑雪场上的各元素值来得到答案
System.out.println(answer(map));
in.close();
out.flush();
}
private static int answer(int[][] map) {
int[][] dp = new int[n][m];//生成动态规划数组
// for(int i=0;i<m;i++) Arrays.fill(dp[i],1);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
//通过dfs每一个点来获得符合要求的最远路径长度
dfs(i, j, map, dp);
}
}
int res = 0;//计算每一个点满足条件的最大长度,注意每次需要更新
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
//比较每一个点动态规划后的长度和此前最长的长度哪个更长,保留更长值
res = Math.max(res, dp[i][j]); //保留有最长路径的那个点的值
}
}
return res;//返回结果
}
private static int dfs( int i, int j, int[][] map, int[][] dp) {
if (i < 0 || j < 0 || i >= n ||
j >= m) { //需要判断是否超过边界,初始点是从0,0开始的
return 0;
}
if (dp[i][j] !=
0) { //如果此点已经得到了其最远路径值那就没有必要再进行dfs了,直接返回其长度即可,这样计算会更加方便,比如25到了21,21往下的数据已经计算过了,就可以直接加上,就不需要再进行遍历
return dp[i][j];
}
int max = 1;//滑道本身也是一个距离,所以要从1开始计算
for (int d = 0; d < 4; d++) {
int newx = i + dx[d]; //dfs过程
int newy = j + dy[d]; //dfs过程
if (newx >= 0 && newx < n && newy >= 0 && newy < m &&
map[newx][newy] <
map[i][j]) {//判断边界及高度应是递减,如果往四个方向走都没有满足条件的,那么当前点的滑道自然就是其本身,这就是为什么每次需要重新赋值1的原因
max = Math.max(max, dfs(newx, newy, map,
dp) + 1); //判断当前点能够到达的最远距离,例如25->24->23->22->21......,每次通过dfs进行判断下一个点(newx,newy)是否能够往下走,这个过程就可以将每次传送过来的点所能够到达的最远距离存储在dp[i][j]里
}//max每次都是在第一个初始点的基础上累加的,如果当前点它可以一直满足条件往下走,那么通过dfs(newx,newy,map,dp)+1这条语句便可以做到累加
}
//给每一个点赋值动态规划后所获得的满足条件的最大路径长度
dp[i][j] = max;
return max;//将当前点的最大路径长度返回
}
}
3、abb
存储每个字符出现的次数,再按顺序进行匹配,因为abb格式,所以要注意遍历的终点边界,且由于是abb型,后边的字符如果不能出现2+次数,那么也不可能匹配成功,注意规律:
例如 ,b 生成 bcc,bcc,bcc,bcc ,此处的b分别是1下标的b与3下标的b,c分别是下标2,4,5的c
即第一个b后三个c,3*2/2=3,第二个b后两个c,2*1/2=1,3+1=4,详细见注释
描述
leafee 最近爱上了 abb 型语句,比如“叠词词”、“恶心心”
leafee 拿到了一个只含有小写字母的字符串,她想知道有多少个 "abb" 型的子序列?
定义: abb 型字符串满足以下条件:
- 字符串长度为 3 。
- 字符串后两位相同。
- 字符串前两位不同。
输入描述:
第一行一个正整数 n
第二行一个长度为 n 的字符串(只包含小写字母)
1≤n≤105
输出描述:
"abb" 型的子序列个数。
示例1
输入:
6 abcbcc
输出:
8
说明:
共有1个abb,3个acc,4个bcc
示例2
输入:
4 abbb
输出:
3
import java.util.*;
public class Main {
static long sum = 0L;
public static void main(String[]args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
String s = sc.next();
int[]word = new int[26]; //26字母数组
for (int i = 0; i < n; i++) {
word[s.charAt(i) - 'a']++; //每个字母出现的次数
}
for (int i = 0; i < n - 2;i++) { //因为是计算abb型序列有多少,那么长度不能少于3,所以遍历到倒数第三个字符即可
int wordi = s.charAt(i) - 'a'; //得到当前字符对应的下标
word[wordi]--;//针对每个字符判断其后边的字符有哪些是可以达到2个以上的,当然不包括它本身,因为遍历往后走,所以当前字符判断以后要减1
for (int j = 0; j < 26;
j++) { //将当前字符与所有字符进行匹配,只有出现2次及以上的才可以匹配,不包括它本身
if (word[j] >= 2 && j != wordi) {
sum += word[j] * (word[j] - 1)/2; //例如 b 生成 bcc,bcc,bcc,bcc ,此处的b分别是1下标的b与3下标的b,c分别是下标2,4,5的c
//即第一个b后三个c,3*2/2=3,第二个b后两个c,2*1/2=1,3+1=4
}
}
}
System.out.println(sum);
}
}
4、小红取数
描述
小红拿到了一个数组,她想取一些数使得取的数之和尽可能大,但要求这个和必须是 k 的倍数。
你能帮帮她吗?
输入描述:
第一行输入两个正整数 n 和 k
第二行输入 n 个正整数 ai
1≤n,k≤103
1≤ai≤1010
输出描述:
如果没有合法方案,输出 -1。
否则输出最大的和。
示例1
输入:
5 5 4 8 2 9 1
输出:
20
说明:
取后四个数即可