前言:别放弃
入门
ZJ18 万万没想到之聪明的编辑
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
int n = sc.nextInt();
for (int i = 0; i < n; i++) {
String next = sc.next();
StringBuilder sb = new StringBuilder(next);
int j = 0; // 初始化起始位置
while(j < sb.length()){
int end = j+1; // 设置当前字母后不连续字母位置
while(end < sb.length() && sb.charAt(j) == sb.charAt(end)){
// 若出现第一个字符相等则后移
end++;
}
if(end - j > 2){
// 连续出现3个同样字母以上,删到只剩两个
sb.delete(j+2, end);
end = j+2;
}
if(end - j == 2 && j >= 2 && sb.charAt(j-1) == sb.charAt(j-2)){
// 出现2个同样字母,检测若是AABB,删除后一个
sb.deleteCharAt(j+1);
}
j++;
}
System.out.println(sb.toString());
}
}
}
}
ZJ23 找零
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[] coins = new int[]{1, 4, 16, 64};
final int total = 1024;
while (sc.hasNext()) {
int N = sc.nextInt();
int n = total - N;
int[] dp = new int[n + 1]; // dp[j]表示凑成j元的最小硬币数,一维数组好的情况下用时稍微少一点,而二维数组就固定死了
for (int j = 1; j <= n; j++) {
dp[j] = n + 1; // 没取硬币想凑成j元是不可能的,且题目要找最小硬币数,设置一个合适值n+1就可以
}
for (int j = 1; j <= n; j++) {
for (int coin : coins) {
if (j >= coin) {
// 一维数组不取值进行比较就默认使用上一层的值
dp[j] = Math.min(dp[j], dp[j - coin] + 1);
}
}
}
System.out.println(dp[n]);
/* int[][] dp = new int[coins.length + 1][n + 1]; // dp[i][j]表示前i个硬币凑成j元的最小硬币数
for (int j = 1; j <= n; j++) {
dp[0][j] = n + 1;
}
// 先容积后取包
for (int j = 1; j <= n; j++) {
for (int i = 1; i <= coins.length; i++) {
if (j >= coins[i - 1]) {
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - coins[i - 1]] + 1);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
// 先取包后容积
for (int i = 1; i <= coins.length; i++) {
for (int j = 1; j <= n; j++) { // 这里的j一定到从1开始取
if (j >= coins[i - 1]) {
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - coins[i - 1]] + 1);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
System.out.println(dp[coins.length][n]);*/
}
}
}
简单
ZJ16 数列的和
数列的和
注:String.format("%.2f",res) 保留小数位
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int n = sc.nextInt();
int m = sc.nextInt();
double res = 0.;
double start = n;
for (int i = 0; i < m; i++) {
res += start;
start = Math.sqrt(start);
}
System.out.println(String.format("%.2f", res));
}
}
}
ZJ17 水仙花数
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int m = sc.nextInt();
int n = sc.nextInt();
StringBuilder sb = new StringBuilder();
for(int i = m; i <= n; i++){
String s = String.valueOf(i);
int sum = 0;
for(int j = 0; j < s.length(); j++){
int tmp = Integer.parseInt(s.substring(j, j + 1));
sum += Math.pow(tmp, 3);
}
if(i == sum){
sb.append(s + " ");
}
}
if(sb.length() == 0){
System.out.println("no");
}else{
String trim = sb.toString().trim(); // 去掉两边的空字符
System.out.println(trim);
}
}
}
}
ZJ19 万万没想到之抓捕孔连顺
注:这道题利用了二分查找
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int n = sc.nextInt();
int d = sc.nextInt();
int[] di = new int[n];
for (int i = 0; i < n; i++) {
di[i] = sc.nextInt();
}
long res = 0L;
for(int i = 0; i < n; i++){ // 二分查找出离选定位置i距离不超过D的最远位置
int target = di[i]+d; // 目标值
int start = i, end = n-1; // 初始寻找范围
int pos = 0; // 目标位置
while(start <= end){
int mid = start + (end-start)/2;
if(di[mid] <= target){
pos = mid;
start = mid+1;
}else{
end = mid-1;
}
}
int l = pos - i; // 第i位置埋伏,其他l个位置中根据组合选2个
//System.out.println(i + " " + end + " " + l);
res = (res + Cnm(l))%99997867;
}
System.out.println(res);
}
}
// 找出C(l,2)组合数
private static long Cnm(long l) {
if(l < 2) // 小于2无法安放两个位置
return 0;
if(l == 2)
return 1;
return l*(l-1)/2;
}
}
ZJ20 雀魂启动
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[] state = new int[9], helpArr = new int[9]; // 按顺序存放1-9张牌出现次数
ArrayList<Integer> res = new ArrayList<>();
for (int i = 0; i < 13; i++) {
int num = sc.nextInt();
state[num - 1]++;
}
for (int i = 0; i < 9; i++) {
if (state[i] < 4) {
// 只要某张牌未出现4次,都可以再添加一张凑成14张牌,判断是否胡牌
int num = i + 1; // 第3位置即表示牌4
System.arraycopy(state, 0, helpArr, 0, 9);
helpArr[i]++; // 该位置添加一张牌
if (canHu(helpArr, 14, false))
res.add(num);
}
}
String outString = null;
if (res.isEmpty())
outString = "";// 若没胡过牌
else {
StringBuilder sb = new StringBuilder();
for(Integer element : res){
sb.append(element + " ");
}
outString = sb.toString().trim();
}
System.out.println(outString);
}
private static boolean canHu(int[] arr, int total, boolean hasHead) {
if (total == 0) // 若牌按照一个雀头,剩下顺子或刻子的规则取完则算胡牌
return true;
if (!hasHead) { // 还未选择两张牌做雀头
for (int i = 0; i < 9; i++) {
if (arr[i] >= 2) {
arr[i] -= 2; //抽两张牌做雀头
if (canHu(arr, total - 2, true))
return true;
// 不能胡牌回溯,重新选择两张牌做雀头,恢复原状态
arr[i] += 2;
}
}
}else{
// 已经选择两张牌做雀头
for (int i = 0; i < 9; i++) {
if (arr[i] > 0) {
// 只有仍存在牌才有取出顺子或刻子的可能
if (arr[i] >= 3) {
// 若一张牌出现3次,则取出来做刻子
arr[i] -= 3;
if (canHu(arr, total - 3, true))
return true;
// 不能胡牌回溯
arr[i] += 3;
}else if (i + 2 < 9 && arr[i + 1] > 0 && arr[i + 2] > 0) {
// 若一张牌没有出现3次,但范围介于1-7间,且找到了后两张牌,可以用来做顺子
arr[i]--;
arr[i + 1]--;
arr[i + 2]--;
if (canHu(arr, total - 3, true))
return true;
// 不能胡牌回溯
arr[i]++;
arr[i + 1]++;
arr[i + 2]++;
}
}
}
}
return false;// 遍历完了仍没有胡牌
}
}
中等
ZJ27 字典序
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()){
long n = sc.nextLong();
long m = sc.nextLong();
long i = 1;
m--; // 找第一个结点,那么就是1
while(m != 0){
long start = i, end = i+1;
long num = 0;
while(start <= n){
num += Math.min(end, n+1)-start;
start *= 10;
end *= 10;
}
if(m < num){
// 在当前节点下,继续向下查找 ,小于的原因是num就包含当前i结点,而m是当前节点的下一个结点开始数
i *= 10;
m--;
}else{
// 往右兄弟结点寻找
i++;
m -= num;
}
}
System.out.println(i);
}
}
}
ZJ26 异或
ZJ26 异或
字典树,以后还需了解线段树
字节真题 ZJ26-异或:使用字典树代替暴力破解降低时间复杂度
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
TrieTree tree = new TrieTree(Integer.toBinaryString((int) 1e5).length());
while(sc.hasNext()){
int n = sc.nextInt();
int m = sc.nextInt();
long total = 0;
//插入A1
tree.insert(sc.nextInt());
for(int i = 0; i < n - 1; i ++){
int me = sc.nextInt();
//找出和Ax异或后结果大于m的数有多少个,并且累加
total += tree.compare(me, m);
//插入Ax
tree.insert(me);
}
//输出结果
System.out.println(total);
}
}
private static class Node{
//有多少个数有当前前缀
public int count = 1;
//子节点
public Node[] child = new Node[2];
}
private static class TrieTree{
//根节点
Node root;
// 总共比较32位,但是最高符号位不用比较,所以最多只需31位,0-30
int bit;
public TrieTree(int bit){
//根节点不包含信息,卫星数据
this.root = new Node();
this.bit = bit;
}
public int compare(int Ax, int m){
return compare(root, Ax, m, bit);
}
public void insert(int tar){
insert(tar, bit);
}
// now : 当前前缀树中,需要开始比较的节点
// tar : 将要插入的数,但是在调用insert插入之前,要和已经插入的数比较(当前前缀树)
// m : 要大于的那个树
// bit : 当前节点的儿子们表示的是对第几位的比较
private int compare(Node now, int tar, int m, int bit){
//逐位比较
for(int i = bit; i >= 0; i --){
if(now == null){
//空节点 表示两者相同 情况5
return 0;
}
int res = (tar >>> i) & 1;
//存在能够 XOR 出 1 的路径, 情况1或2
if((now.child[res ^ 1]) != null){
//如果目标的当前位是 0,说明异或结果已经小于 Ax
if(((m >>> i) & 1) == 0){
//情况1
// 但是不能单纯只返回异或结果大于m的那条路径上的分支数量
//因为当前位异或结果相等于m的那条路径上的分支可能还存在满足异或结果大于m的情况
return now.child[res ^ 1].count + compare(now.child[res], tar, m, i - 1);
} else {
//情况2
// 异或结果相等 接着找
now = now.child[res ^ 1];
}
} else{
//情况3
//异或结果小于 m, 直接返回0
if(((m >>> i) & 1) == 1){
return 0;
}
//情况4
// 异或结果相等,接着找
now = now.child[res];
}
}
//默认返回0
return 0;
}
private void insert(int tar, int bit){
Node now = root;
root.count ++;
//从第二位开始,忽略最高为第一位,所有输入都是正数,忽略最高符号位
for(int i = bit; i >= 0; i --){
//获取要插入的数的第(32 - i )位
int res = (tar >>> i) & 1;
//如果之前不存在节点,则新建节点,count 默认 = 1
if(now.child[res] == null){
now.child[res] = new Node();
now = now.child[res];
}else{
//如果之前已经有节点存在,则count++
now = now.child[res];
now.count ++;
}
}
}
}
}