├── README.md └── problems ├── 0001.A+B问题I.md ├── 0002.A+B问题II.md ├── 0003.A+B问题III.md ├── 0004.A+B问题IV.md ├── 0005.A+B问题VII.md ├── 0006.A+B问题VIII.md ├── 0007.平均绩点.md ├── 0008.摆平积木.md ├── 0009.奇怪的信.md ├── 0010.运营商活动.md ├── 0011.共同祖先.md ├── 0012.打印数字图形.md ├── 0013.镂空三角形.md ├── 0014.句子缩写.md ├── 0015.神秘字符.md ├── 0016.位置互换.md ├── 0017.出栈合法性.md ├── 0018.链表的基本操作.md ├── 0019.单链表反转.md ├── 0020.删除重复元素.md ├── 0021.构造二叉树.md ├── 0022.二叉树的遍历.md ├── 0023.二叉树的高度.md ├── 0024.最长公共子序列.md ├── 0025.最爱的城市.md ├── 0026.不相同的字符串.md ├── 0027.最长增长子序列.md ├── 0028.子序列中的k种字母.md ├── 0029.安排超市.md ├── 0030.字符串解压缩.md ├── 0031.字符串的最大价值.md ├── 0032.子矩阵的最大面积.md ├── 0033.逛街.md ├── 0034.大鱼吃小鱼.md ├── 0035.打印二维数组.md ├── 0036.网格路径和.md ├── 0037.交换字符.md ├── 0038.填充矩阵I.md ├── 0039.求和.md ├── 0040.到达目的地的最短距离.md ├── 0041.岛屿数量.md ├── 0042.路径简化.md ├── 0043.汽水瓶换饮料.md ├── 0044.开发商购买土地.md ├── 0045.虚拟棋盘对战.md ├── 0046.携带研究材料.md ├── 0047.参会dijkstra堆.md ├── 0047.参会dijkstra朴素.md ├── 0047.参加科学大会.md ├── 0048.安排讲座.md ├── 0049.整数的不同位数.md ├── 0050.随机数排序.md ├── 0051.平移二叉树.md ├── 0052.携带研究材料II.md ├── 0053.寻宝-Kruskal.md ├── 0053.寻宝-prim.md ├── 0053.寻宝.md ├── 0054.替换数字.md ├── 0055.右旋字符串.md ├── 0056.携带矿石资源.md ├── 0057.爬楼梯.md ├── 0058.区间和.md ├── 0059.判断是否是树.md ├── 0060.匹配前缀的词典.md ├── 0061.出现一次的整数.md ├── 0062.平方差.md ├── 0063.更小的数.md ├── 0064.颜色平衡树.md ├── 0065.买瓜.md ├── 0066.网络稳定性.md ├── 0067.异或和之和.md ├── 0068.像素放置.md ├── 0069.翻转硬币.md ├── 0070.冶炼金属.md ├── 0071.飞机降落.md ├── 0072.接龙数列.md ├── 0073.岛屿个数.md ├── 0074.子串简写.md ├── 0075.整数删除.md ├── 0076.景区导游.md ├── 0077.砍树.md ├── 0078.三国游戏.md ├── 0079.填充.md ├── 0080.翻转.md ├── 0081.子矩阵.md ├── 0082.互质数的个数.md ├── 0083.异或和值之差.md ├── 0084.公因数匹配.md ├── 0085.子树的大小.md ├── 0086.近似GCD.md ├── 0087.数组个数.md ├── 0088.打折.md ├── 0089.松散子序列.md ├── 0090.管道.md ├── 0091.保险箱.md ├── 0092.砝码称重.md ├── 0093.括号序列.md ├── 0094.城市间货物运输I-SPFA.md ├── 0094.城市间货物运输I.md ├── 0095.城市间货物运输II.md ├── 0096.城市间货物运输III.md ├── 0097.小明逛公园.md ├── 0098.所有可达路径.md ├── 0099.岛屿的数量广搜.md ├── 0099.岛屿的数量深搜.md ├── 0100.岛屿的最大面积.md ├── 0101.孤岛的总面积.md ├── 0102.沉没孤岛.md ├── 0103.水流问题.md ├── 0104.建造最大岛屿.md ├── 0105.有向图的完全可达性.md ├── 0106.岛屿的周长.md ├── 0107.寻找存在的路径.md ├── 0108.冗余连接.md ├── 0109.冗余连接II.md ├── 0110.字符串接龙.md ├── 0111.构造二阶行列式.md ├── 0112.挑战boss.md ├── 0113.国际象棋.md ├── 0114.小欧的平均数.md ├── 0115.组装手机.md ├── 0117.软件构建.md ├── 0121.大数减法.md ├── 0121.小红的区间翻转.md ├── 0122.滑动窗口最大值.md ├── 0123.小红的数组构造.md ├── 0124.精华帖子.md ├── 0125.连续子数组最大和.md ├── 0126.骑士的攻击astar.md ├── 0127.小美的排列询问.md ├── 0128.小美走公路.md ├── 0129.小美的蛋糕切割.md ├── 0130.小美的字符串变换.md ├── 0131.小美的树上染色.md ├── 0132.夹吃旗.md ├── 0133.小红买药.md ├── 0134.皇后移动的最小步数.md ├── 0135.获取连通的相邻节点列表.md ├── 0136.字符串处理器.md ├── 0137.消息传输.md ├── 0139.可爱串.md ├── 0139.完美数.md ├── 0141.好二叉树.md ├── 0142.两个字符串的最小ASCII删除总和.md ├── 0143.最长同值路径.md ├── 0144.字典序最小的01字符串.md ├── 0145.数组子序列的排列.md ├── 0146.传送树.md ├── 0147.三珠互斥.md ├── 0148.扑克牌同花顺.md ├── 0149.好数组.md ├── 0150.极长连续段的权值.md ├── 0151.手机流畅运行的秘密.md ├── 0152.小米手机通信校准.md ├── 0153.权值优势路径计数.md ├── 0154.序列中位数.md ├── 0155.最小化频率的删除代价.md ├── 0156.勇敢牛牛战斗序列.md ├── 0157.最大化密码复杂度.md ├── 0158.同余方程.md ├── 0159.大整数乘法.md ├── 0160.二维平面上的折线段.md ├── 0161.讨厌鬼的组合帖子.md ├── 0162.小红的第16版方案.md ├── 0163.优秀数组.md ├── 0164.升序数组.md ├── 0165.最大字典序无重复串.md ├── 0166.照明灯安装.md ├── 0167.黑白块.md ├── 0168.删除三元组.md ├── 0169.非连续合法字符串.md ├── 0170.权值不等的路径方案.md ├── 0171.卡牌对弈.md ├── 0172.股票上涨序列.md ├── 0173.编辑距离内的子串.md ├── 0174.魔物入侵.md ├── 0175.阴阳师.md ├── 0176.切割正数.md ├── 0177.学习语言.md ├── 0178.寻找平均数.md ├── 0183.有效的重复字符.md ├── 0184.米小游和蹦蹦史莱姆.md ├── 0185.米小游买手办.md ├── 0186.米小游和建木.md ├── 0187.小欧新建文件夹.md ├── 0188.小欧的等差数列.md ├── 0189.小欧的弹球.md ├── 0190.偏爱的字符.md ├── 0191.小明打砖块.md ├── 0192.游游的you子串.md ├── 0193.游游的树上操作.md ├── 0194.游游的数组推平.md ├── 0195.游游的数组压缩.md ├── 0196.小美的MT.md ├── 0197.小美的数组询问.md ├── 0198.小美的平衡矩阵.md ├── 0199.小美的区间删除.md ├── 0200.小美的朋友关系.md ├── 0201.支付宝消费打折.md ├── 0202.小红切字符串.md ├── 0203.小苯的GCD.md ├── 0204.陨石撞地球.md ├── 0205.小A的授权请求.md ├── 0206.开幕式排练.md ├── 0207.最少数字.md ├── 0208.小红的二叉树构造.md ├── 0209.小红的节点染色.md ├── 0210.最长合法子串长度.md ├── 0211.小苯的文章浏览.md ├── 0212.小苯的粉丝关注.md ├── 0213.小苯的点赞.md ├── 0214.小红的数组访问.md ├── 0215.小苯的数组染色.md ├── 0216.小红的区间.md ├── 0217.小美的01矩阵.md ├── 0218.小美的回文子串.md ├── 0219.小美的元素交换.md ├── 0220.小美的字符串切割.md ├── 0221.迷路的孩子.md ├── 0222.计算出非回文子串长度.md ├── 0223.排列字串搜索.md ├── 0224.计算最高运行效率.md ├── 0225.字符-数字卡片拼电话号码.md ├── 0226.最接近的三数之和.md ├── 0227.藻类的总重量.md ├── 0228.吃豆人游戏.md ├── 0229.平衡子串的长度.md ├── 0230.Alice和Bob的数字游戏.md ├── 0231.伊文的字符串.md ├── 0232.多多的回文修建.md ├── 0233.超级快递点.md ├── 0234.消灭怪物得最高分.md ├── 0235.图形周长.md ├── 0236.最短区间.md ├── 0237.小红的二叉树路径权值.md ├── 0238.小红的路径统计.md ├── 0239.小红的剪切线.md ├── 0240.小红小紫寻宝.md ├── 0241.小杰打怪物.md ├── 0242.小杰的排列.md ├── 0243.小杰的数字和.md ├── 0244.小杰的好字符串.md ├── 0245.云服务计费.md ├── 0246.相似图片分类.md ├── 0247.网络保卫战.md ├── 0248.小苯点灯.md ├── 0249.小红的互斥数组.md └── 0250.小红远征.md /problems/0001.A+B问题I.md: -------------------------------------------------------------------------------- 1 | 2 | # 1. A+B问题I 3 | 4 | 欢迎提交PR,贡献其他语言版本 5 | 6 | [题目链接](https://kamacoder.com/problempage.php?pid=1000) 7 | 8 | 9 | ## C++ 10 | 11 | ```CPP 12 | #include 13 | using namespace std; 14 | int main() { 15 | int a, b; 16 | while (cin >> a >> b) cout << a + b << endl; 17 | } 18 | ``` 19 | 20 | ## Java 21 | 22 | ```Java 23 | import java.lang.*; 24 | import java.util.*; 25 | 26 | public class Main{ 27 | public static void main(String[] args){ 28 | Scanner in = new Scanner(System.in); 29 | while(in.hasNextInt()){ 30 | int a = in.nextInt(); 31 | int b = in.nextInt(); 32 | System.out.println(a+b); 33 | } 34 | } 35 | } 36 | ``` 37 | 38 | ## python 39 | 40 | ```python 41 | import sys 42 | 43 | for line in sys.stdin: 44 | a, b = line.split(' ') 45 | print(int(a) + int(b)) 46 | ``` 47 | 48 | ## Go 49 | 50 | ```Go 51 | package main 52 | 53 | import "fmt" 54 | 55 | func main(){ 56 | var a, b int 57 | for { 58 | _, err := fmt.Scanf("%d %d",&a, &b) 59 | if err != nil { 60 | break 61 | } 62 | fmt.Println(a + b) 63 | } 64 | } 65 | ``` 66 | 67 | ## Js 68 | 69 | ```Js 70 | // 引入readline模块来读取标准输入 71 | const readline = require('readline'); 72 | 73 | // 创建readline接口 74 | const rl = readline.createInterface({ 75 | input: process.stdin, 76 | output: process.stdout 77 | }); 78 | 79 | // 处理输入和输出 80 | function processInput() { 81 | rl.on('line', (input) => { 82 | // 将输入按空格分割成a和b的数组 83 | const [a, b] = input.split(' ').map(Number); 84 | 85 | // 计算a和b的和并输出 86 | const sum = a + b; 87 | console.log(sum); 88 | }); 89 | } 90 | 91 | // 开始处理输入 92 | processInput(); 93 | ``` 94 | 95 | ## C 96 | 97 | ```C 98 | #include 99 | int main() { 100 | int a, b; 101 | while (scanf("%d%d", &a, &b) == 2) { 102 | int sum = a + b; 103 | printf("%d\n", sum); 104 | } 105 | return 0; 106 | } 107 | ``` 108 | 109 | ## PHP 110 | 111 | ```php 112 | = input.split(' ').collect(); 143 | let a: u32 = parts[0].trim().parse().expect("无法转换为数字"); 144 | let b: u32 = parts[1].trim().parse().expect("无法转换为数字"); 145 | 146 | println!("{}", a + b); 147 | } 148 | } 149 | ``` 150 | -------------------------------------------------------------------------------- /problems/0002.A+B问题II.md: -------------------------------------------------------------------------------- 1 | 2 | # 2.A+B问题II 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?id=1001) 5 | 6 | ## C++ 7 | 8 | ```CPP 9 | #include 10 | using namespace std; 11 | int main() { 12 | int n, a, b; 13 | while (cin >> n) { 14 | while (n--) { 15 | cin >> a >> b; 16 | cout << a + b << endl; 17 | } 18 | } 19 | } 20 | ``` 21 | 22 | ## Java 23 | 24 | ```Java 25 | 26 | import java.util.Scanner; 27 | 28 | public class Main { 29 | public static void main(String[] args) { 30 | Scanner scanner = new Scanner(System.in); 31 | while (scanner.hasNext()) { 32 | int n = scanner.nextInt(); 33 | while (n-- > 0) { 34 | int a = scanner.nextInt(); 35 | int b = scanner.nextInt(); 36 | System.out.println(a + b); 37 | } 38 | } 39 | } 40 | } 41 | ``` 42 | 43 | ## python 44 | 45 | ```python 46 | while 1: 47 | try: 48 | N = int(input()) 49 | for i in range(N): 50 | l = list(map(int,input().split())) 51 | print(sum(l)) 52 | except: 53 | break 54 | ``` 55 | 56 | ## Go 57 | 58 | ```Go 59 | package main 60 | 61 | import "fmt" 62 | 63 | func main() { 64 | var n, a, b int 65 | for { 66 | _, err := fmt.Scan(&n) 67 | if err != nil { 68 | break 69 | } 70 | for n > 0 { 71 | _, err := fmt.Scan(&a, &b) 72 | if err != nil { 73 | break 74 | } 75 | fmt.Println(a + b) 76 | n-- 77 | } 78 | } 79 | } 80 | ``` 81 | 82 | ## Js 83 | 84 | ```Js 85 | // 引入readline模块来读取标准输入 86 | const readline = require("readline"); 87 | 88 | // 创建readline接口 89 | const rl = readline.createInterface({ 90 | input: process.stdin, 91 | output: process.stdout, 92 | }); 93 | 94 | function processInput() { 95 | let pathArr = []; 96 | rl.on("line", (input) => { 97 | let path = input.split(" ").map(Number); 98 | // 将输入转为数组,根据length属性判断输入数字的个数 99 | if (path.length == 1) { 100 | pathArr.push(path); 101 | } else { 102 | const [a, b] = path 103 | const sum = a + b; 104 | console.log(sum); 105 | } 106 | }); 107 | 108 | } 109 | processInput(); 110 | ``` 111 | 112 | ## C 113 | 114 | ```C 115 | #include 116 | int main() 117 | { 118 | int n; 119 | while(scanf("%d",&n)!=EOF) 120 | { 121 | while(n--) 122 | { 123 | int a,b; 124 | scanf("%d %d",&a,&b); 125 | printf("%d\n",a+b); 126 | } 127 | } 128 | return 0; 129 | } 130 | ``` 131 | -------------------------------------------------------------------------------- /problems/0003.A+B问题III.md: -------------------------------------------------------------------------------- 1 | 2 | # 3. A+B问题III 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1002) 5 | 6 | ## C++ 7 | 8 | ```CPP 9 | #include 10 | using namespace std; 11 | int main() { 12 | int a, b; 13 | while (cin >> a >> b) { 14 | if (a == 0 && b == 0) break; 15 | cout << a + b << endl; 16 | } 17 | } 18 | ``` 19 | ## Java 20 | 21 | ```Java 22 | import java.util.Scanner; 23 | 24 | public class Main { 25 | public static void main(String[] args) { 26 | Scanner scanner = new Scanner(System.in); 27 | while (scanner.hasNext()) { 28 | int a = scanner.nextInt(); 29 | int b = scanner.nextInt(); 30 | if (a == 0 && b == 0) { 31 | break; 32 | } 33 | System.out.println(a + b); 34 | } 35 | } 36 | } 37 | 38 | ``` 39 | 40 | ## python 41 | 42 | ```python 43 | import sys 44 | 45 | while True: 46 | s = input().split() # 一行一行读取 47 | a, b = int(s[0]), int(s[1]) 48 | if not a and not b: # 遇到 0, 0 则中断 49 | break 50 | print(a + b) 51 | ``` 52 | 53 | ## Go 54 | 55 | ```Go 56 | package main 57 | 58 | import "fmt" 59 | 60 | func main() { 61 | var a, b int 62 | for { 63 | _, err := fmt.Scan(&a, &b) 64 | if err != nil { 65 | break 66 | } 67 | if a == 0 && b == 0 { 68 | break 69 | } 70 | fmt.Println(a + b) 71 | } 72 | } 73 | ``` 74 | 75 | ## Js 76 | 77 | ```Js 78 | // 引入readline模块来读取标准输入 79 | const readline = require('readline'); 80 | 81 | // 创建readline接口 82 | const rl = readline.createInterface({ 83 | input: process.stdin, 84 | output: process.stdout 85 | }); 86 | 87 | function preoceeInput() { 88 | rl.on('line', (input) => { 89 | const [a, b] = input.split(' ').map(Number); 90 | // # 遇到 0, 0 则中断 91 | if (a === 0 && b === 0) { 92 | return; 93 | } else { 94 | console.log(a + b); 95 | } 96 | }); 97 | } 98 | 99 | preoceeInput() 100 | ``` 101 | 102 | ```JavaScript 103 | // 使用 Node.js 的 readline 模块来模拟 C++ 中的 cin 和 cout 操作 104 | function main() { 105 | // 导入readline模块 106 | const readline = require('readline'); 107 | 108 | // 创建readline接口 109 | const rl = readline.createInterface({ 110 | input: process.stdin, // 从标准输入读取数据 111 | output: process.stdout // 将输出写入标准输出 112 | }); 113 | 114 | // 监听用户的输入事件 115 | rl.on('line', (input) => { 116 | // 将输入拆分为两个数,并将其转换为数字 117 | const [a, b] = input.split(' ').map(Number); 118 | 119 | // 判断输入的两个数是否都为0 120 | if (a === 0 && b === 0) { 121 | rl.close(); // 如果是,则关闭输入流,结束程序 122 | } else { 123 | console.log(a + b); // 否则,计算并输出两数之和 124 | } 125 | }); 126 | } 127 | 128 | main(); // 调用主函数开始程序的执行 129 | ``` 130 | 131 | ## C 132 | 133 | ```C 134 | #include 135 | 136 | int main() 137 | { 138 | int a,b; 139 | while(scanf("%d %d",&a,&b)) 140 | { 141 | if(a==0 && b==0) 142 | break; 143 | printf("%d\n",a+b); 144 | } 145 | return 0; 146 | } 147 | ``` 148 | -------------------------------------------------------------------------------- /problems/0004.A+B问题IV.md: -------------------------------------------------------------------------------- 1 | 2 | # 4. A+B问题IV 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1003) 5 | 6 | ## C++ 7 | 8 | ```CPP 9 | #include 10 | using namespace std; 11 | int main(){ 12 | int n, a; 13 | while (cin >> n) { 14 | if (n == 0) break; 15 | int sum = 0; 16 | while (n--) { 17 | cin >> a; 18 | sum += a; 19 | } 20 | cout << sum << endl; 21 | } 22 | } 23 | ``` 24 | ## Java 25 | 26 | ```Java 27 | import java.util.Scanner; 28 | 29 | public class Main { 30 | public static void main(String[] args) { 31 | Scanner scanner = new Scanner(System.in); 32 | while (scanner.hasNext()) { 33 | int n = scanner.nextInt(); 34 | if (n == 0) { 35 | break; 36 | } 37 | int sum = 0; 38 | for (int i = 0; i < n; i++) { 39 | sum += scanner.nextInt(); 40 | } 41 | System.out.println(sum); 42 | } 43 | 44 | ``` 45 | 46 | ## python 47 | 48 | ```python 49 | import sys 50 | 51 | for line in sys.stdin: 52 | nums = line.split() 53 | nums = list(map(int, nums)) 54 | n = nums[0] 55 | if not n: 56 | break 57 | print( sum(nums[-n:]) ) 58 | ``` 59 | 60 | ## Go 61 | 62 | ```Go 63 | package main 64 | 65 | import "fmt" 66 | 67 | func main() { 68 | var n, a int 69 | for { 70 | _, err := fmt.Scan(&n) 71 | if err != nil { 72 | break 73 | } 74 | if n == 0 { 75 | break 76 | } 77 | sum := 0 78 | for n > 0 { 79 | _, err := fmt.Scan(&a) 80 | if err != nil { 81 | break 82 | } 83 | sum += a 84 | n-- 85 | } 86 | fmt.Println(sum) 87 | } 88 | } 89 | ``` 90 | 91 | ## Js 92 | 93 | ```JS 94 | // 引入readline模块来读取标准输入 95 | const readline = require('readline'); 96 | 97 | // 创建readline接口 98 | const rl = readline.createInterface({ 99 | input: process.stdin, 100 | output: process.stdout 101 | }); 102 | 103 | function preoceeInput() { 104 | rl.on('line', (input) => { 105 | // 读入每行数据,将其转换为数组 106 | const line = input.split(' ').map(Number); 107 | // 判断读入的第一个数字是否为0 108 | if (line[0] === 0) { 109 | return; 110 | } else { 111 | let sum = 0; 112 | for (let i = 1; i < line[0] + 1; i++) { 113 | sum += line[i]; 114 | } 115 | console.log(sum); 116 | } 117 | }); 118 | } 119 | 120 | preoceeInput() 121 | ``` 122 | 123 | ## C 124 | 125 | ```C 126 | #include 127 | 128 | int main() 129 | { 130 | int n; 131 | while(scanf("%d",&n)) 132 | { 133 | if(n==0) 134 | break; 135 | int nums[n]; 136 | int result=0; 137 | for(int i=0;i 10 | using namespace std; 11 | int main() { 12 | int a, b; 13 | while (cin >> a >> b) cout << a + b << endl << endl; 14 | } 15 | ``` 16 | ## Java 17 | 18 | ```Java 19 | import java.util.Scanner; 20 | public class Main{ 21 | public static void main(String[] args){ 22 | Scanner sc = new Scanner(System.in); 23 | while(sc.hasNextLine()){ 24 | int a = sc.nextInt(); 25 | int b = sc.nextInt(); 26 | System.out.println(a + b); 27 | System.out.println(); 28 | } 29 | } 30 | } 31 | ``` 32 | 33 | ## python 34 | 35 | ```python 36 | while True: 37 | try: 38 | x, y = map(int, (input().split())) 39 | print(x + y) 40 | print() 41 | except: 42 | break 43 | ``` 44 | 45 | ## Go 46 | 47 | ```go 48 | package main 49 | 50 | import "fmt" 51 | 52 | func main() { 53 | var a, b int 54 | for { 55 | _, err := fmt.Scan(&a, &b) 56 | if err != nil { 57 | break 58 | } 59 | fmt.Printf("%d\n\n", a+b) 60 | } 61 | } 62 | 63 | ``` 64 | 65 | ## Js 66 | ```javascript 67 | const readline=require('readline'); 68 | const rl=readline.createInterface({ 69 | input:process.stdin, 70 | output:process.stdout 71 | }); 72 | function Sum(){ 73 | rl.on("line",(input)=>{ 74 | const [a,b]=input.split(' ').map(Number); 75 | console.log(a+b+"\n"); 76 | }); 77 | } 78 | Sum(); 79 | ``` 80 | ## C 81 | 82 | 83 | ```C 84 | #include 85 | 86 | int main() 87 | { 88 | int a, b; 89 | while(scanf("%d%d",&a,&b)!=EOF){ 90 | printf("%d\n\n",a+b); 91 | } 92 | return 0; 93 | } 94 | ``` 95 | -------------------------------------------------------------------------------- /problems/0009.奇怪的信.md: -------------------------------------------------------------------------------- 1 | # 9. 奇怪的信 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1008) 4 | 5 | ## C++ 6 | 7 | ```CPP 8 | #include 9 | using namespace std; 10 | int main() { 11 | int n, a; 12 | while (cin >> n) { 13 | int result = 0; 14 | while (n != 0) { 15 | a = (n % 10); 16 | n = n / 10; 17 | if (a % 2 == 0) result += a; 18 | } 19 | cout << result << endl; 20 | cout << endl; 21 | } 22 | } 23 | ``` 24 | 25 | ## Java 26 | 27 | ```Java 28 | import java.util.*; 29 | 30 | public class Main { 31 | public static void main(String[] args) { 32 | Scanner in = new Scanner(System.in); 33 | while (in.hasNextInt()) { 34 | int n = in.nextInt(); 35 | int res = 0; 36 | while (n > 0) { 37 | int tmp = n % 10; 38 | if (tmp % 2 == 0) { 39 | res += tmp; 40 | } 41 | n /= 10; 42 | } 43 | System.out.println(res); 44 | System.out.println(); 45 | } 46 | } 47 | } 48 | ``` 49 | 50 | ## python 51 | 52 | ```python 53 | while 1: 54 | try: 55 | n=input() 56 | s=0 57 | for i in n: 58 | if int(i)%2 == 0: 59 | s += int(i) 60 | print(s) 61 | print() 62 | except: 63 | break 64 | ``` 65 | 66 | ## Go 67 | 68 | ```Go 69 | package main 70 | 71 | import ( 72 | "fmt" 73 | ) 74 | 75 | func main() { 76 | var n, a, result int 77 | for { 78 | _, err := fmt.Scanf("%d", &n) 79 | if err != nil || n == 0 { 80 | break 81 | } 82 | result = 0 83 | for n != 0 { 84 | a = n % 10 85 | n = n / 10 86 | if a%2 == 0 { 87 | result += a 88 | } 89 | } 90 | fmt.Println(result) 91 | fmt.Println() 92 | } 93 | } 94 | ``` 95 | 96 | ## Js 97 | 98 | ```js 99 | const readline = require("readline"); 100 | const r1 = readline.createInterface({ 101 | input: process.stdin, 102 | output: process.stdout, 103 | }); 104 | 105 | const iter = r1[Symbol.asyncIterator](); 106 | 107 | const read_line = async () => (await iter.next()).value; 108 | 109 | let line = null; 110 | 111 | (async function () { 112 | while ((line = await read_line())) { 113 | const arr = line.split("").map((item) => Number(item)); 114 | let sum = 0; 115 | for (let i = 0; i < arr.length; i++) { 116 | if (arr[i] % 2 === 0) { 117 | sum += arr[i]; 118 | } 119 | } 120 | console.log(sum, "\n"); 121 | } 122 | })(); 123 | ``` 124 | 125 | ## C 126 | 127 | ```C 128 | #include 129 | 130 | int main() { 131 | int n, a; 132 | while (scanf("%d", &n) == 1) { 133 | int result = 0; 134 | while (n != 0) { 135 | a = n % 10; 136 | n = n / 10; 137 | if (a % 2 == 0) result += a; 138 | } 139 | printf("%d\n\n", result); 140 | } 141 | return 0; 142 | } 143 | ``` 144 | -------------------------------------------------------------------------------- /problems/0030.字符串解压缩.md: -------------------------------------------------------------------------------- 1 | # 30. 字符串解压缩 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1030) 4 | 5 | ## C++ 6 | ```c++ 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | 12 | string solve(string s) { 13 | // 记录[ ] |的位置,每次总是会找到最内层的三个 14 | // 若未找到说明已经完全解压缩,直接返回s 15 | int x, y, z; 16 | x = y = z = -1; 17 | for(int i = 0; i < s.size(); ++i) { 18 | if(s[i] == '[') { 19 | x = i; 20 | } 21 | else if(s[i] == '|') { 22 | y = i; 23 | } 24 | else if(s[i] == ']') { 25 | z = i; 26 | break; 27 | } 28 | } 29 | 30 | // 必须三个都判断是否全部找到,排除[ ] |作为常规字符的情况 31 | if(x != -1 && y != -1 && z != -1) { 32 | // 取出重复数字 33 | istringstream ss(s.substr(x + 1, y - x)); 34 | int num; 35 | ss >> num; 36 | // 将需要重复的字符串取出并且叠加 37 | string repeat = ""; 38 | string tmp = s.substr(y + 1, z - y - 1); 39 | for(int i = 0; i < num; ++i) { 40 | repeat.append(tmp); 41 | } 42 | // 前段未处理的部分 43 | string pre = s.substr(0, x); 44 | // 后段未处理的部分 45 | string end = s.substr(z + 1); 46 | // 与重复的部分组合进行递归 47 | return solve(pre + repeat + end); 48 | } 49 | 50 | return s; 51 | 52 | } 53 | 54 | int main() { 55 | string s; 56 | // 这里一定要getline, 否则遇到空格会断掉读取 57 | while(getline(cin, s)) { 58 | string ans = solve(s); 59 | cout << ans << endl; 60 | } 61 | return 0; 62 | } 63 | ``` 64 | ## Java 65 | 66 | ```java 67 | import java.lang.*; 68 | import java.util.*; 69 | 70 | /* 71 | 72 | 字符串操作,考察设计递归函数的能力。 73 | 74 | 实现步骤如下: 75 | 76 | 遍历编码后的字符串,通过记录'['、'|'和']'的位置,找到每个需要重复的子字符串以及重复次数。 77 | 78 | 如果找到了一组完整的重复字符串信息(即找到了'['、'|'和']'),就将这段信息提取出来,然后根据重复次数和子字符串,构建新的解码后的字符串。 79 | 80 | 递归地对新构建的解码字符串进行解码,直到没有重复字符串信息为止。 81 | 82 | */ 83 | 84 | public class Main { 85 | public static void main(String[] args) { 86 | Scanner scanner = new Scanner(System.in); 87 | String str = scanner.nextLine(); 88 | System.out.print(decode(str)); 89 | } 90 | 91 | public static String decode(String s) { 92 | int i = 0; 93 | int x = -1, y = -1, z = -1; 94 | 95 | while (i < s.length()) { 96 | if (s.charAt(i) == '[') { 97 | x = i; 98 | } else if (s.charAt(i) == '|') { 99 | y = i; 100 | } else if (s.charAt(i) == ']') { 101 | z = i; 102 | break; 103 | } 104 | i++; 105 | } 106 | 107 | if (x != -1 && y != -1 && z != -1) { 108 | int times = Integer.parseInt(s.substring(x + 1, y)); 109 | String sub = s.substring(y + 1, z); 110 | StringBuilder decodeStrBuilder = new StringBuilder(); 111 | 112 | for (int j = 0; j < times; j++) { 113 | decodeStrBuilder.append(sub); 114 | } 115 | 116 | String decodeStr = s.substring(0, x) + decodeStrBuilder.toString() + s.substring(z + 1); 117 | return decode(decodeStr); 118 | } 119 | return s; 120 | } 121 | } 122 | ``` 123 | 124 | ## Python 125 | 126 | ## Go 127 | 128 | ## JS 129 | 130 | ## C 131 | -------------------------------------------------------------------------------- /problems/0050.随机数排序.md: -------------------------------------------------------------------------------- 1 | # 50. 随机数排序 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1050) 4 | 5 | ## C++ 6 | ```c++ 7 | // 因为随机数的数字范围是1-500 8 | // 所以可以采取桶排序 9 | // 读取所有数据,如果某个数出现了,就赋值为1 10 | // 最后遍历数组,将值为1,即出现过的数字按顺序输出 11 | #include 12 | using namespace std; 13 | 14 | int n; 15 | 16 | void solve() { 17 | int N = 501; 18 | vector bucket(N, 0); // 储存出现过的数字,0代表未出现,1代表出现 19 | // 读入数据并将出现的数字标记为1 20 | while(n--) { 21 | int tmp; 22 | cin >> tmp; 23 | bucket[tmp] = 1; 24 | } 25 | // 按顺序输出出现过的数字 26 | for(int i = 0; i < N; ++i) { 27 | if(bucket[i]) { 28 | cout << i << " "; 29 | } 30 | } 31 | cout << endl; 32 | } 33 | 34 | 35 | int main() { 36 | while(cin >> n) { 37 | solve(); 38 | } 39 | return 0; 40 | } 41 | ``` 42 | ## Java 43 | 44 | ```java 45 | /** 46 | * 使用 Java 集合特性 47 | */ 48 | import java.util.*; 49 | 50 | public class Main { 51 | public static void main(String[] args) { 52 | Scanner scanner = new Scanner(System.in); 53 | int n = scanner.nextInt(); 54 | Set uniqueNumbers = new TreeSet<>();// TreeSet能够自动去重 + 自动排序 55 | for (int i = 0; i < n; i++) { 56 | int num = scanner.nextInt(); 57 | uniqueNumbers.add(num); 58 | } 59 | for (int num : uniqueNumbers) { 60 | System.out.printf("%d ", num); 61 | } 62 | System.out.println(); 63 | } 64 | } 65 | ``` 66 | 67 | ## Python 68 | 69 | ```python 70 | _ = input() 71 | print(' '.join(map(str, sorted(set(map(int,input().split())))))) 72 | ``` 73 | 74 | ## Go 75 | 76 | ## JS 77 | 78 | ## C 79 | -------------------------------------------------------------------------------- /problems/0052.携带研究材料II.md: -------------------------------------------------------------------------------- 1 | # 52. 携带研究材料 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1052) 4 | 5 | ## C++ 6 | 7 | ```cpp 8 | #include 9 | #include 10 | using namespace std; 11 | 12 | // 先遍历背包,再遍历物品 13 | void test_CompletePack(vector weight, vector value, int bagWeight) { 14 | 15 | vector dp(bagWeight + 1, 0); 16 | 17 | for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量 18 | for(int i = 0; i < weight.size(); i++) { // 遍历物品 19 | if (j - weight[i] >= 0) dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); 20 | } 21 | } 22 | cout << dp[bagWeight] << endl; 23 | } 24 | int main() { 25 | int N, V; 26 | cin >> N >> V; 27 | vector weight; 28 | vector value; 29 | for (int i = 0; i < N; i++) { 30 | int w; 31 | int v; 32 | cin >> w >> v; 33 | weight.push_back(w); 34 | value.push_back(v); 35 | } 36 | test_CompletePack(weight, value, V); 37 | return 0; 38 | } 39 | ``` 40 | 41 | ## Java 42 | 43 | ```java 44 | import java.util.*; 45 | 46 | public class Main { 47 | 48 | public static void main(String[] args) { 49 | Scanner sc = new Scanner(System.in); 50 | int N = sc.nextInt(), V = sc.nextInt(); 51 | int[] weights = new int[N], vals = new int[N]; 52 | for (int i = 0; i < N; i++) { 53 | weights[i] = sc.nextInt(); 54 | vals[i] = sc.nextInt(); 55 | } 56 | helper(weights, vals, V); 57 | sc.close(); 58 | } 59 | 60 | static void helper(int[] weights, int[] vals, int bagSize) { 61 | int[] dp = new int[bagSize + 1]; 62 | 63 | for (int j = 0; j <= bagSize; j++) { // 遍历背包 64 | for (int i = 0; i < weights.length; i++) { // 遍历物品 65 | if (j >= weights[i]) 66 | dp[j] = Math.max(dp[j], dp[j - weights[i]] + vals[i]); 67 | } 68 | } 69 | 70 | System.out.println(dp[bagSize]); 71 | } 72 | } 73 | ``` 74 | 75 | 76 | 77 | ## Python 78 | 79 | ```python 80 | from sys import stdin 81 | N, V = map(int, input().split()) 82 | arr = [tuple(map(int, line.split())) for line in stdin] 83 | arr.sort() 84 | 85 | dp = [0]*(V+1) 86 | for w, v in arr: 87 | for j in range(w, V+1): 88 | dp[j] = max(dp[j], dp[j-w] + v) 89 | print(dp[-1]) 90 | ``` 91 | 92 | ## Go 93 | 94 | ## JS 95 | 96 | ## C -------------------------------------------------------------------------------- /problems/0056.携带矿石资源.md: -------------------------------------------------------------------------------- 1 | # 56. 携带矿石资源 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1066) 4 | 5 | ## C++ 6 | ```C++ 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | int c,n; // 容量 和 种类数量 12 | void chose(){ 13 | vectorweight(n); 14 | vectorvalue(n); 15 | vectornums(n); 16 | for(int i = 0;i < n;i++){ 17 | cin >> weight[i]; 18 | } 19 | for(int i = 0;i < n;i++){ 20 | cin >> value[i]; 21 | } 22 | for(int i = 0;i < n;i++){ 23 | cin >> nums[i]; 24 | } 25 | 26 | //标准的01背包 27 | vectordp(c+1,0); 28 | for(int i = 0;i < n;i++){ 29 | for(int j = c;j >= weight[i];j--){ 30 | for(int k = 0;k <= nums[i] && (j - k*weight[i]) >= 0;k++){ 31 | dp[j] = max(dp[j], dp[j-k*weight[i]] + k*value[i]); 32 | } 33 | 34 | } 35 | } 36 | cout << dp[c] << endl; 37 | } 38 | 39 | 40 | int main(){ 41 | cin >> c >> n; 42 | chose(); 43 | return 0; 44 | } 45 | ``` 46 | ## Java 47 | 48 | ```java 49 | import java.util.Scanner; 50 | 51 | public class Main { 52 | public static void main(String[] args) { 53 | Scanner scanner = new Scanner(System.in); 54 | int C = scanner.nextInt(); 55 | int N = scanner.nextInt(); 56 | 57 | int[] weights = new int[N]; 58 | int[] values = new int[N]; 59 | int[] nums = new int[N]; 60 | 61 | for (int i = 0; i < N; i++) { 62 | weights[i] = scanner.nextInt(); 63 | } 64 | for (int i = 0; i < N; i++) { 65 | values[i] = scanner.nextInt(); 66 | } 67 | for (int i = 0; i < N; i++) { 68 | nums[i] = scanner.nextInt(); 69 | } 70 | 71 | int[] dp = new int[C + 1]; 72 | for(int i = 0; i < weights.length; i++) { // 遍历物品 73 | for(int j = C; j >= weights[i]; j--) { // 遍历背包容量 74 | // 以上为01背包,然后加一个遍历个数 75 | for (int k = 1; k <= nums[i] && (j - k * weights[i]) >= 0; k++) { // 遍历个数 76 | dp[j] = Math.max(dp[j], dp[j - k * weights[i]] + k * values[i]); 77 | } 78 | } 79 | } 80 | System.out.println(dp[C]); 81 | } 82 | } 83 | ``` 84 | 85 | ## Python 86 | 87 | ## JS 88 | 89 | ## Go 90 | 91 | ## C 92 | -------------------------------------------------------------------------------- /problems/0057.爬楼梯.md: -------------------------------------------------------------------------------- 1 | # 57. 爬楼梯 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1067) 4 | 5 | ## C++ 6 | ```C++ 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | int main() { 12 | int n, m; 13 | while (cin >> n >> m) { 14 | vector dp(n + 1, 0); 15 | dp[0] = 1; 16 | for (int i = 1; i <= n; i++) { // 遍历物品 17 | for (int j = 1; j <= m; j++) { // 遍历背包 18 | if (i - j >= 0) dp[i] += dp[i - j]; 19 | } 20 | } 21 | cout << dp[n] << endl; 22 | } 23 | } 24 | ``` 25 | ## Java 26 | 27 | ```java 28 | import java.util.Arrays; 29 | import java.util.Scanner; 30 | 31 | public class Main { 32 | 33 | public static int climbStairs(int n, int m) { 34 | int[] dp = new int[n + 1]; 35 | Arrays.fill(dp, 0); 36 | dp[0] = 1; 37 | for (int i = 1; i <= n; i++) { 38 | for (int j = 1; j <= m; j++) { 39 | if (i - j >= 0) { 40 | dp[i] += dp[i - j]; 41 | } 42 | } 43 | } 44 | return dp[n]; 45 | } 46 | public static void main(String[] args) { 47 | Scanner scanner = new Scanner(System.in); 48 | int n = scanner.nextInt(); 49 | int m = scanner.nextInt(); 50 | System.out.println(climbStairs(n, m)); 51 | } 52 | } 53 | ``` 54 | 55 | ## Python 56 | ```Python 57 | n,m = input().split(' ') 58 | n = int(n) 59 | m = int(m) 60 | dp = [0] * (n + 1) 61 | dp[0] = 1 62 | for i in range(1,n+1): 63 | for j in range(1,m+1): 64 | if (i - j >= 0): 65 | dp[i] += dp[i - j] 66 | 67 | print(dp[n]) 68 | 69 | ``` 70 | ## JS 71 | ```JS 72 | // 引入readline模块,并定义rl对象的输入输出流接口 73 | const readline = require('readline'); 74 | const rl = readline.createInterface({ 75 | input:process.stdin, 76 | output:process.stdout 77 | }) 78 | // rl对象监听IO行输入事件,每行只有一个参数 79 | rl.on('line', (s) => { 80 | let [n,m] = s.split(' ') 81 | // console.log("input", n, m) 82 | console.log(climbNStairsByM(n,m)) 83 | }) 84 | 85 | // 爬楼梯 86 | function climbNStairsByM(n, m){ 87 | // 初始化dp数组 88 | const dp = []; 89 | dp[0] = 1; 90 | dp[1] = 1; 91 | 92 | for(let i = 2; i <= n; i++ ) { 93 | 94 | //状态转移方程 95 | dp[i] = 0; 96 | for(let j = 1; j <= m; j++) { 97 | if (j > i) break; // i-j为负数后就不用算了 98 | dp[i] += dp[i - j]; 99 | } 100 | 101 | } 102 | return dp[n]; 103 | } 104 | ``` 105 | ## Go 106 | ```Go 107 | package main 108 | import ( 109 | "fmt" 110 | ) 111 | 112 | func main(){ 113 | var n,m int 114 | fmt.Scanf("%d %d",&n,&m) 115 | var dp = make([]int,n + 1) 116 | dp[0] = 1 117 | for i := 1;i <= n;i++ { 118 | for j := 1;j <= m;j++ { 119 | if i - j >= 0 { 120 | dp[i] += dp[i - j] 121 | } 122 | } 123 | } 124 | fmt.Println(dp[n]) 125 | 126 | } 127 | ``` 128 | 129 | ## C 130 | -------------------------------------------------------------------------------- /problems/0059.判断是否是树.md: -------------------------------------------------------------------------------- 1 | # 59.判断是否是树 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1071) 4 | 5 | ## C 6 | 7 | ## C++ 8 | ### 并查集做法 9 | ```cpp 10 | #include 11 | 12 | using namespace std; 13 | 14 | const int N = 1010; 15 | int p[N]; 16 | 17 | // 查询祖先节点 18 | int find(int x) 19 | { 20 | if (x != p[x]) p[x] = find(p[x]); 21 | return p[x]; 22 | } 23 | 24 | int main() 25 | { 26 | int n; 27 | cin >> n; 28 | for (int i = 0; i < n; i ++) p[i] = i; 29 | for (int i = 1; i < n; i ++) 30 | { 31 | int a, b; 32 | cin >> a >> b; 33 | int pa = find(a), pb = find(b); 34 | if (pa == pb) 35 | { 36 | cout << "false" << endl; 37 | return 0; 38 | } 39 | p[pa] = pb; 40 | } 41 | cout << "true" << endl; 42 | return 0; 43 | } 44 | ``` 45 | 46 | ## Java 47 | 48 | ```java 49 | import java.util.Scanner; 50 | 51 | class UnionFind { 52 | private int[] parent; 53 | 54 | public UnionFind(int size) { 55 | parent = new int[size]; 56 | for (int i = 0; i < size; i++) { 57 | parent[i] = i; 58 | } 59 | } 60 | 61 | public int find(int x) { 62 | if (parent[x] == x) return x; 63 | return parent[x] = find(parent[x]); // 路径压缩 64 | } 65 | 66 | public void union(int x, int y) { 67 | int u = find(x); 68 | int v = find(y); 69 | if (u != v) { 70 | parent[v] = u; 71 | } 72 | } 73 | } 74 | 75 | public class Main { 76 | public static void main(String[] args) { 77 | Scanner scanner = new Scanner(System.in); 78 | int n = scanner.nextInt(); 79 | UnionFind unionFind = new UnionFind(n); 80 | 81 | for (int i = 0; i < n - 1; i++) { 82 | int start = scanner.nextInt(); 83 | int end = scanner.nextInt(); 84 | if (unionFind.find(start) == unionFind.find(end)) { 85 | System.out.println("false"); // 有环 86 | return; 87 | } 88 | unionFind.union(start, end); 89 | } 90 | 91 | int rootCount = 0; 92 | for (int i = 0; i < n; i++) { 93 | if (unionFind.find(i) == i) { 94 | rootCount++; 95 | } 96 | } 97 | 98 | boolean isTree = rootCount == 1; 99 | System.out.println(isTree); 100 | } 101 | } 102 | ``` 103 | 104 | ## Python 105 | 106 | ## JS 107 | 108 | ## Go 109 | ### 并查集做法 110 | ```go 111 | package main 112 | 113 | import "fmt" 114 | 115 | func main(){ 116 | 117 | var n int 118 | fmt.Scan(&n) 119 | p := make([]int, n) 120 | for i := 0; i < n; i ++ { 121 | p[i] = i 122 | } 123 | 124 | // 路径压缩的并查集 125 | var find func(int) int 126 | find = func(x int) int { 127 | if x != p[x]{ 128 | p[x] = find(p[x]) 129 | } 130 | return p[x] 131 | } 132 | for i := 1; i < n; i ++{ 133 | var a, b int 134 | fmt.Scan(&a, &b) 135 | // 找到a、b的祖先节点 136 | pa, pb := find(a), find(b) 137 | // 判断祖先节点是否相等,若相等则说明它们之前已经是在一棵树里面了 138 | // 再次加边则会破坏树结构 139 | if pa == pb{ 140 | fmt.Println("false") 141 | return 142 | } 143 | // 合并a、b所在的两个集合 144 | p[pa] = pb 145 | } 146 | fmt.Println("true") 147 | } 148 | ``` 149 | -------------------------------------------------------------------------------- /problems/0060.匹配前缀的词典.md: -------------------------------------------------------------------------------- 1 | # 60. 匹配前缀的词典 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1072) 4 | 5 | ## C 6 | 7 | ## C++ 8 | 9 | ## Java 10 | 11 | ```java 12 | import java.util.Scanner; 13 | 14 | // 字典树的节点 15 | class TrieNode { 16 | boolean isWord; 17 | final TrieNode[] children; 18 | public TrieNode() { 19 | isWord = false; 20 | children = new TrieNode[26]; 21 | } 22 | } 23 | 24 | // 字典树 25 | class Trie { 26 | TrieNode root; 27 | 28 | Trie() { 29 | root = new TrieNode(); 30 | } 31 | 32 | public void insert(String word) { 33 | TrieNode current = root; 34 | for (int i = 0; i < word.length(); i++) { 35 | int c = word.charAt(i) - 'a'; 36 | if (current.children[c] == null) { 37 | current.children[c] = new TrieNode(); 38 | } 39 | current = current.children[c]; 40 | } 41 | current.isWord = true; 42 | } 43 | 44 | // 完整的字段树应该包含搜索功能,但是此处用不到 45 | // public boolean search(String word) { 46 | // TrieNode current = root; 47 | // for (int i = 0; i < word.length(); i++) { 48 | // int c = word.charAt(i) - 'a'; 49 | // if (current.children[c] == null) { 50 | // return false; 51 | // } 52 | // current = current.children[c]; 53 | // } 54 | // return current.isWord; 55 | // } 56 | 57 | public boolean startWith(String word) { 58 | TrieNode current = root; 59 | for (int i = 0; i < word.length(); i++) { 60 | int c = word.charAt(i) - 'a'; 61 | if (current.children[c] == null) { 62 | return false; 63 | } 64 | current = current.children[c]; 65 | } 66 | return true; 67 | } 68 | } 69 | 70 | public class Main { 71 | public static void main(String[] args) { 72 | Scanner scanner = new Scanner(System.in); 73 | int m = scanner.nextInt(); 74 | int n = scanner.nextInt(); 75 | 76 | scanner.nextLine(); // 消耗掉换行符 77 | Trie trie = new Trie(); 78 | for (int i = 0; i < m; i++) { 79 | trie.insert(scanner.nextLine()); 80 | } 81 | 82 | for (int i = 0; i < n; i++) { 83 | boolean isStart = trie.startWith(scanner.nextLine()); 84 | System.out.println(isStart); 85 | } 86 | } 87 | } 88 | ``` 89 | 90 | ## Python 91 | 92 | ## JS 93 | 94 | ## Go 95 | -------------------------------------------------------------------------------- /problems/0061.出现一次的整数.md: -------------------------------------------------------------------------------- 1 | # 61. 出现一次的整数 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1073) 4 | 5 | ## C 6 | ```C 7 | #include 8 | int main() 9 | { 10 | int n;scanf("%d",&n); 11 | int ans=0; 12 | while(n--) 13 | { 14 | int x;scanf("%d",&x); 15 | ans^=x; 16 | } 17 | printf("%d\n",ans); 18 | return 0; 19 | } 20 | ``` 21 | ## C++ 22 | ```CPP 23 | #include 24 | using namespace std; 25 | int main() 26 | { 27 | int n;cin>>n; 28 | int ans=0; 29 | while(n--) 30 | { 31 | int x;cin>>x; 32 | ans^=x; 33 | } 34 | cout<0{ 88 | var x int 89 | fmt.Scan(&x) 90 | ans^=x; 91 | n--; 92 | } 93 | fmt.Println(ans); 94 | } 95 | ``` 96 | -------------------------------------------------------------------------------- /problems/0062.平方差.md: -------------------------------------------------------------------------------- 1 | # 62. 平方差 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1101) 4 | 5 | ## C 6 | 7 | ## C++ 8 | ```CPP 9 | #include 10 | using namespace std; 11 | int main() 12 | { 13 | int l,r;cin>>l>>r; 14 | unordered_map map; 15 | int ans=0; 16 | for(int y=0;y<=1000;y++) 17 | { 18 | for(int z=0;z<=1000;z++) 19 | { 20 | int x=y*y-z*z; 21 | if(x>=l&&x<=r&&map[x]==0) 22 | { 23 | // cout< 10 | 11 | using namespace std; 12 | 13 | int main() 14 | { 15 | string str;cin>>str; 16 | int ans=0; 17 | for(int i=0;inums[i+1]: 52 | dp[i][i+1] = True 53 | # 第一层循环遍历需要反转的数组长度,第二层循环遍历起始位置 54 | for L in range(3,length+1): 55 | for i in range(length-L+1): 56 | if nums[i] > nums[i+L-1]: 57 | dp[i][i+L-1] = True 58 | elif nums[i] == nums[i+L-1]: 59 | dp[i][i+L-1] = dp[i+1][i+L-2] 60 | else: 61 | dp[i][i+L-1] = False 62 | # 统计答案个数 63 | for i in range(length): 64 | for j in range(i,length): 65 | if dp[i][j]: ans +=1 66 | 67 | print(ans) 68 | 69 | ``` 70 | 71 | ## JS 72 | 73 | ## Go 74 | -------------------------------------------------------------------------------- /problems/0064.颜色平衡树.md: -------------------------------------------------------------------------------- 1 | # 64. 颜色平衡树 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1103) 4 | 5 | ## C 6 | 7 | ## C++ 8 | 9 | ## Java 10 | 11 | ## Python 12 | 13 | ## JS 14 | 15 | ## Go 16 | -------------------------------------------------------------------------------- /problems/0066.网络稳定性.md: -------------------------------------------------------------------------------- 1 | # 66. 网络稳定性 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1105) 4 | 5 | ## C 6 | 7 | ## C++ 8 | ```C++ 9 | 10 | #include 11 | using namespace std; 12 | typedef long long LL; 13 | const int N = 100010; 14 | 15 | int n; 16 | int a[N][25]; 17 | int main() 18 | { 19 | ios_base :: sync_with_stdio(false); 20 | cin.tie(0); cout.tie(0); 21 | cin >> n; 22 | for (int i = 1; i <= n; ++i) { 23 | int x; 24 | cin >> x; 25 | for (int j = 0; j <= 20; ++j) { 26 | a[i][j] = (x >> j) & 1; 27 | a[i][j] ^= a[i - 1][j]; 28 | } 29 | } 30 | LL ans = 0; 31 | for (int j = 0; j <= 20; ++j) { 32 | map m; 33 | m[0]++; 34 | for (int i = 1; i <= n; ++i) { 35 | int x = m[a[i][j] ^ 1]; 36 | ans += 1LL * (1 << j) * x; 37 | m[a[i][j]]++; 38 | } 39 | } 40 | cout << ans << '\n'; 41 | return 0; 42 | } 43 | 44 | ``` 45 | ## Java 46 | 47 | ## Python 48 | 49 | ## JS 50 | 51 | ## Go 52 | -------------------------------------------------------------------------------- /problems/0067.异或和之和.md: -------------------------------------------------------------------------------- 1 | # 67. 异或和之和 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1106) 4 | 5 | ## C 6 | 7 | ## C++ 8 | ```cpp 9 | #include 10 | #define rep(i, n) for (int i = 0; i < (n); ++i) 11 | 12 | using namespace std; 13 | using ll = long long; 14 | 15 | int main() { 16 | cin.tie(nullptr) -> sync_with_stdio(false); 17 | 18 | int n; 19 | cin >> n; 20 | 21 | vector a(n+1); 22 | for (int i = 1; i <= n; ++i) { 23 | cin >> a[i]; 24 | a[i] ^= a[i-1]; 25 | } 26 | 27 | ll ans = 0; 28 | vector c(2); 29 | rep(b, 21) { 30 | c[0] = c[1] = 0; 31 | ll now = 0; 32 | rep(i, n+1) { 33 | c[a[i]>>b&1]++; 34 | ans += 1ll*c[a[i]>>b&1^1]<> j) & 1; 58 | a[i][j] ^= a[i - 1][j]; 59 | } 60 | } 61 | long ans = 0; 62 | for (int j = 0; j <= 20; ++j) { 63 | Map m = new HashMap<>(); 64 | m.put(0, 1); 65 | for (int i = 1; i <= n; ++i) { 66 | int x = m.getOrDefault(a[i][j] ^ 1, 0); 67 | ans += (1L << j) * x; 68 | m.put(a[i][j], m.getOrDefault(a[i][j], 0) + 1); 69 | } 70 | } 71 | System.out.println(ans); 72 | } 73 | } 74 | 75 | ``` 76 | ## Python 77 | ```python 78 | N = int(input()) 79 | nums = [int(i) for i in input().split()] 80 | n = len(nums) 81 | xor_sum = 0 82 | for i in range(n): 83 | # 子段的异或和 84 | segment_xor = 0 85 | for j in range(i, n): 86 | # i到j的异或和 = i到j-1的异或和 XOR j 87 | segment_xor ^= nums[j] 88 | xor_sum += segment_xor 89 | print(xor_sum) 90 | ``` 91 | ## JS 92 | 93 | ## Go 94 | -------------------------------------------------------------------------------- /problems/0069.翻转硬币.md: -------------------------------------------------------------------------------- 1 | # 69. 翻转硬币 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1108) 4 | 5 | ## C 6 | 7 | ## C++ 8 | 9 | ## Java 10 | 11 | ## Python 12 | 13 | ## JS 14 | 15 | ## Go 16 | -------------------------------------------------------------------------------- /problems/0070.冶炼金属.md: -------------------------------------------------------------------------------- 1 | # 70. 冶炼金属 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1109) 4 | 5 | ## C 6 | 7 | ## C++ 8 | ```C++ 9 | //两种做题思路,一种是直接根据题目意思直接通过公式算,另外一种方法是只能通过理解得出一个公式,后面由二分自己算出 10 | //第一种 11 | #include 12 | 13 | using namespace std; 14 | 15 | int main() 16 | { 17 | int n,a,b,res1=INT_MAX,res2=0; 18 | cin>>n; 19 | while(n--) 20 | { 21 | cin>>a>>b; 22 | res1=min(res1,a/b); 23 | res2=max(res2,a/(b+1)); 24 | } 25 | cout< 31 | 32 | using namespace std; 33 | 34 | int main() 35 | { 36 | vector v; 37 | int max=10000011; 38 | int n;cin>>n; 39 | for(int i=0;i>x>>y; 43 | v.push_back(x); 44 | v.push_back(y); 45 | int c=x/y; 46 | if(c>1; 59 | if((x/mid)>y) 60 | { 61 | l=mid; 62 | } 63 | else 64 | { 65 | r=mid-1; 66 | } 67 | } 68 | if(l+1>min) 69 | { 70 | min=l+1; 71 | } 72 | } 73 | cout< 10 | 11 | using namespace std; 12 | 13 | const int N=10; 14 | 15 | int n; 16 | struct Plane 17 | { 18 | int t,d,l; 19 | }p[N]; 20 | bool st[N]; 21 | 22 | bool dfs(int u,int last) //当前的点以及上一个的时间点 23 | { 24 | if(u==n) return true; 25 | 26 | for(int i=0;i=last) 30 | { 31 | st[i]=true; 32 | if(dfs(u+1,max(last,t)+l)) 33 | return true; 34 | st[i]=false; 35 | } 36 | } 37 | return false; 38 | } 39 | 40 | 41 | int main() 42 | { 43 | int T;cin>>T; 44 | 45 | while(T--) 46 | { 47 | cin>>n; 48 | for(int i=0;i>t>>d>>l; 51 | p[i]={t,d,l}; 52 | 53 | } 54 | memset(st,0,sizeof st); 55 | if(dfs(0,0)) puts("YES"); 56 | else puts("NO"); 57 | 58 | } 59 | return 0; 60 | } 61 | ``` 62 | ## Java 63 | 64 | ## Python 65 | 66 | ## JS 67 | 68 | ## Go 69 | -------------------------------------------------------------------------------- /problems/0072.接龙数列.md: -------------------------------------------------------------------------------- 1 | # 72. 接龙数列 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1111) 4 | 5 | ## C 6 | 7 | ## C++ 8 | ```cpp 9 | #include 10 | 11 | using namespace std; //这个其实就是最长子序列的改版 12 | const int N=10010; 13 | int arr[N]; 14 | int f[N]; 15 | 16 | bool check(int x,int y) 17 | { 18 | string str1=to_string(x); 19 | string str2=to_string(y); 20 | if(str1[str1.size()-1]==str2[0]) 21 | return true; 22 | return false; 23 | } 24 | 25 | int main() 26 | { 27 | int n;cin>>n; 28 | for(int i=1;i<=n;i++)cin>>arr[i]; 29 | 30 | for(int i=1;i<=n;i++) 31 | { //假设只有一个是符合的 32 | f[i]=1; 33 | for(int j=1;j<=i;j++) 34 | { //拿arr[j]和arr[i]进行一个满足比较,如果符合规律 35 | if(i==j) 36 | continue; 37 | if(check(arr[j],arr[i])) 38 | { 39 | f[i]=max(f[i],f[j]+1); 40 | } //要搞清楚这个f是什么类型的数组,f[i]表示满足接龙数列的数组的最大长度 41 | } 42 | } 43 | int res=0; 44 | for(int i=1;i<=n;i++) 45 | res=max(res,f[i]); 46 | cout< 10 | using namespace std; 11 | const int N=10010; 12 | int arr[N]; //前缀和数组 13 | char a,b; 14 | string str; 15 | int k; 16 | 17 | int main() 18 | { //直接记录前缀和,记录前面有多少个a, 19 | cin>>k;cin>>str>>a>>b; 20 | 21 | for(int i=1;i<=str.size();i++) 22 | { 23 | if(str[i-1]==a) 24 | { 25 | arr[i]=arr[i-1]+1; 26 | }else { 27 | arr[i]=arr[i-1]; 28 | } 29 | } 30 | 31 | long long res=0; 32 | //后面直接找出b字符,然后看他k个前面有多少个a字符 33 | for(int i=0;i=-1)) 36 | { //然后读出sum,加到res中 37 | res+=arr[i-k+2]; 38 | } 39 | } 40 | //这个一开始想错了 41 | //想成是要加上中间有多少个a,固然调试好久,可能是前面一直在 42 | //想题,没放松的原因了 43 | //后面才意识到是在当前字符串中,前面的a字符数量 44 | cout< 10 | using namespace std; 11 | const int N=5e5+10; 12 | int n,k; 13 | priority_queue,vector>,greater>>q; 14 | int pre[N],ne[N]; 15 | long long cnt[N],a[N],t; 16 | int main(){ 17 | scanf("%d%d",&n,&k); 18 | for(int i=1;i<=n;i++){ 19 | scanf("%lld",&t); 20 | q.push({t,i}); 21 | pre[i]=i-1; 22 | ne[i]=i+1; 23 | } 24 | while(q.size()>n-k){ 25 | long long x=q.top().first; //当前的值 26 | int id=q.top().second; //当前的下标 27 | q.pop(); 28 | if(cnt[id]){ 29 | q.push({x+cnt[id],id}); 30 | cnt[id]=0; //这步很细节,保证了后面的元素被删除之后又可以加到这里 31 | } 32 | else{ 33 | int left=pre[id],right=ne[id]; 34 | cnt[left]+=x; 35 | cnt[right]+=x; 36 | ne[left]=right; //保证了左边右边隔断 37 | pre[right]=left; 38 | } 39 | } 40 | while(q.size()){ 41 | long long x=q.top().first; 42 | int id=q.top().second; //表示剩下数字对应的下标 43 | q.pop(); 44 | a[id]=x+cnt[id]; //加上它本应该要加上的内容 45 | } 46 | for(int i=1;i<=n;i++){ 47 | if(a[i]) cout< 11 | using namespace std; 12 | typedef long long ll; 13 | typedef unsigned long long ull; 14 | typedef pair pii; 15 | #define speed_up ios::sync_with_stdio(0);cin.tie(0);cout.tie(0) 16 | #define y1 y__ 17 | #define time t__ 18 | #define lowbit(x) ((x)&(-x)) 19 | 20 | const int N=1e5+10,M=2e5+10; 21 | int e[M],ne[M],idx,h[N],id[M]; 22 | vector query[N]; 23 | int p[N]; 24 | int st[N]; 25 | int n,m; 26 | int d[N]; 27 | int ans=0; 28 | 29 | void add(int a,int b,int seq){ 30 | e[idx]=b,ne[idx]=h[a],id[idx]=seq,h[a]=idx++; 31 | } 32 | 33 | int find(int x){ 34 | if(p[x]!=x)p[x]=find(p[x]); 35 | return p[x]; 36 | } 37 | 38 | void tarjan(int u,int fa){ 39 | st[u]=1; 40 | for(int i=h[u];~i;i=ne[i]){ 41 | int j=e[i]; 42 | if(j==fa || st[j])continue; 43 | tarjan(j,u); 44 | p[j]=u; 45 | } 46 | for(auto t:query[u]){ 47 | if(st[t]==2){ 48 | d[t]++,d[u]++,d[find(t)]-=2; 49 | } 50 | } 51 | st[u]=2; 52 | } 53 | 54 | int dfs(int u,int fa){ 55 | int sum=d[u]; 56 | for(int i=h[u];~i;i=ne[i]){ 57 | int j=e[i]; 58 | if(j==fa)continue; 59 | int temp=dfs(j,u); 60 | if(temp==m)ans=max(ans,id[i]); 61 | sum+=temp; 62 | } 63 | return sum; 64 | } 65 | 66 | int main(){ 67 | speed_up; 68 | memset(h,-1,sizeof h); 69 | cin>>n>>m; 70 | for(int i=1;i<=n;i++)p[i]=i; 71 | for(int i=1;i<=n-1;i++){ 72 | int a,b; 73 | cin>>a>>b; 74 | add(a,b,i),add(b,a,i); 75 | } 76 | for(int i=1;i<=m;i++){ 77 | int a,b; 78 | cin>>a>>b; 79 | query[a].push_back(b); 80 | query[b].push_back(a); 81 | } 82 | tarjan(1,-1); 83 | dfs(1,-1); 84 | if(ans==0)cout<<-1; 85 | else cout< 贪心,排序 6 | 7 | 设wi = Ai - Bi - Ci 的含义为经过第 i 件事情后A国的兵力比 B + C 国的总合多的数量,假如 wi > 0 的话,说明A国经历第 i 件事情后兵力大于B C总和。 8 | 9 | 可以分别计算A,B,C最多经历多少次事件后依旧获胜,然后取最大值即为结果 10 | 11 | 首先计算出wi,之后给wi排序后累加,当wi <= 0 的时候就说明到了最多事件数,再经历事件就不具备获胜条件了。 12 | 13 | ## C 14 | 15 | ## C++ 16 | ```C++ 17 | #include 18 | using namespace std; 19 | typedef long long LL; 20 | const int N=1e5+10; 21 | int n; 22 | int a[N],b[N],c[N]; 23 | int w[N]; //权值数组 24 | int work(int x[],int y[],int z[]) 25 | { 26 | for(int i=0;i()); //从大到小进行排序 29 | LL sum=0; 30 | if(w[0]<=0) return -1; 31 | for(int i=0;i>n; 43 | for(int i=0;i>a[i]; 44 | for(int i=0;i>b[i]; 45 | for(int i=0;i>c[i]; 46 | int res=max({work(a,b,c),work(b,a,c),work(c,b,a)}); //这种初始化列表的方式可以一次性比较多个,c++11新特性 47 | cout<= 0; i--) { 85 | sum += w[i]; 86 | if(sum <= 0) return n - 1 - i; 87 | } 88 | return n; 89 | } 90 | } 91 | ``` 92 | 93 | ## Python 94 | 95 | ## JS 96 | 97 | ## Go 98 | -------------------------------------------------------------------------------- /problems/0079.填充.md: -------------------------------------------------------------------------------- 1 | # 79. 填充 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1119) 4 | 5 | > 贪心 6 | 7 | 从前往后遍历字符串,如果第 i 个字符可以和第 i+1 个字符匹配,就给答案+1,然后跳到第 i+2 的位置继续遍历 8 | 9 | 证明: 10 | 11 | 证明这种方式是最优解如下: 12 | 13 | 假如1,2位置字符匹配的话,让他们组队后有两种情况 14 | 15 | > 3字符和2字符匹配: 16 | 17 | 这个时候如果不让1和2匹配,反而让2和3匹配就相当于浪费了1字符 18 | 19 | > 3字符和2字符不匹配 20 | 21 | 这个时候如果不让1和2匹配,就浪费掉了1和2 22 | 23 | ## C 24 | 25 | ## C++ 26 | ```cpp 27 | #include 28 | 29 | using namespace std; 30 | 31 | 32 | 33 | int main() 34 | { 35 | string s; 36 | cin>>s; 37 | int res=0; 38 | for(int i=0;i+1 13 | using namespace std; 14 | const int N=1e6+10; 15 | char s[N],t[N]; 16 | int main() 17 | { 18 | int T; 19 | cin>>T; 20 | while(T--) 21 | { 22 | scanf("%s%s",t,s); 23 | int n=strlen(s); 24 | int res=0; 25 | for(int i=0;i 11 | using namespace std; 12 | using ll=long long; 13 | const int N=1010,mod=998244353; 14 | 15 | int n,m,a,b; 16 | int w[N][N]; 17 | int rmax[N][N]; //区间最大的值 18 | int rmin[N][N]; //区间最小的值 19 | int q[N]; 20 | 21 | void get_max(int a[],int b[],int tot,int k) 22 | { 23 | int hh=0,tt=-1; 24 | for(int i=0;i=a[i])tt--; 41 | q[++tt]=i; 42 | b[i]=a[q[hh]]; 43 | 44 | } 45 | 46 | 47 | } 48 | 49 | 50 | int main() 51 | { 52 | cin>>n>>m>>a>>b; 53 | 54 | for(int i=0;i>w[i][j]; 60 | } 61 | } 62 | 63 | for(int i=0;i 9 | 10 | using namespace std; 11 | 12 | long long qmi(long long a,long long b,long long p) 13 | { 14 | long long res=1; 15 | while(b) 16 | { 17 | if(b&1) res=res*a%p; 18 | b=b>>1; 19 | a=a*a%p; 20 | } 21 | return res; 22 | } 23 | 24 | int main() 25 | { 26 | long long a,b;cin>>a>>b; 27 | if(a==1) 28 | { 29 | cout<<0<1) ans=ans/c*(c-1); 45 | cout< 双指针 6 | 7 | 题意: 8 | 9 | 我们需要找的子数组需要满足以下两个条件: 10 | 11 | * 数组中最多只有一个元素和 g 的最大公约数不等于 g ,其余的最大公约数都等于 g 12 | * 数组的长度大于等于 2 13 | 14 | 使用双指针法,定义左指针 j 和右指针 i 分别表示:下标以 i 结尾,j ~ i-1开头的子数组都满足条件。 15 | 16 | 具体步骤如下: 17 | 18 | 1. 初始化 j = 0,last = -1。last用于维护记录最新的与 g 最大公约数不为 g 的下标 19 | 2. 遍历数组 nums 当遇到```nums[i] % g != 0```时更新 last 和 j 20 | 3. 累加结果 21 | 22 | 需要注意的点: 23 | 24 | * last 初始化为 -1 的目的是为了满足题意"可以更改数组中的一个元素" 25 | * 每次遍历结果累加(左指针 - 右指针)即可,不需要判断数组长度是否不小于2 26 | * ```nums[i] % g != 0```是用于判断 nums[i] 和 g 的最大公约数是否等于 g 27 | 28 | ## C 29 | 30 | ## C++ 31 | 32 | ```c++ 33 | // 双指针 时间复杂度: O(n) 34 | #include 35 | using namespace std; 36 | int main() 37 | { 38 | int n, g; 39 | cin >> n >> g; 40 | int nums[n]; 41 | for (int i = 0; i < n; i++) { 42 | cin >> nums[i]; 43 | } 44 | int last = -1, j = 0; 45 | long res = 0; 46 | for (int i = 0; i < n; i++) { 47 | if (nums[i] % g != 0) { 48 | j = last + 1; // 左指针更新 49 | last = i; // 记录当前右指针位置 50 | } 51 | res += (i-j); 52 | } 53 | cout << res << endl; 54 | return 0; 55 | } 56 | ``` 57 | 58 | ## Java 59 | 60 | ```java 61 | // 双指针 时间复杂度: O(n) 62 | import java.util.Scanner; 63 | 64 | public class Main { 65 | public static void main(String[] args) { 66 | Scanner scan = new Scanner(System.in); 67 | int n = scan.nextInt(); 68 | long g = scan.nextLong(); 69 | long[] nums = new long[n]; 70 | for (int i = 0; i < n; i++) { 71 | nums[i] = scan.nextLong(); 72 | } 73 | int last = -1, j = 0; 74 | long res = 0; 75 | for (int i = 0; i < n; i++) { 76 | if (nums[i] % g != 0) { 77 | j = last + 1; // 左指针更新 78 | last = i; // 记录当前右指针位置 79 | } 80 | res += (i-j); 81 | } 82 | System.out.println(res); 83 | scan.close(); 84 | } 85 | } 86 | ``` 87 | 88 | ## Python 89 | 90 | ## JS 91 | 92 | ## Go 93 | -------------------------------------------------------------------------------- /problems/0087.数组个数.md: -------------------------------------------------------------------------------- 1 | # 87. 数组个数 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1127) 4 | 5 | ## C 6 | 7 | ## C++ 8 | 9 | ## Java 10 | 11 | ## Python 12 | 13 | ## JS 14 | 15 | ## Go 16 | -------------------------------------------------------------------------------- /problems/0088.打折.md: -------------------------------------------------------------------------------- 1 | # 88. 打折 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1128) 4 | 5 | ## C 6 | 7 | ## C++ 8 | 9 | ## Java 10 | 11 | ## Python 12 | 13 | ## JS 14 | 15 | ## Go 16 | -------------------------------------------------------------------------------- /problems/0089.松散子序列.md: -------------------------------------------------------------------------------- 1 | # 89. 子树的大小 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1129) 4 | 5 | ## C 6 | 7 | ## C++ 8 | ```cpp 9 | //典型的打家舍劫问题,具体请看代码随想录,动规专题 10 | #include 11 | using namespace std; 12 | const int N=1e6+10; 13 | int main() 14 | { 15 | string str; 16 | cin>>str; 17 | int n=str.size(); 18 | vector f(n+2); 19 | 20 | for(int i=0;i 11 | using namespace std; 12 | const int N=100010; 13 | 14 | int n; 15 | char a[N],b[N]; 16 | int f[N][3]; 17 | 18 | int main() 19 | { 20 | scanf("%d%s%s",&n,a,b); 21 | memset(f,0x3f,sizeof f); 22 | f[n][1]=0; 23 | 24 | for(int i=n-1;i>=0;i--) 25 | for(int j=0;j<3;j++) 26 | for(int k=-9;k<=9;k++) 27 | for(int t=0;t<3;t++) 28 | if(a[i]+k+t-1-b[i]==(j-1)*10) 29 | f[i][j]=min(f[i][j],f[i+1][t]+abs(k)); 30 | 31 | cout< 11 | 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | const int N = 16; 17 | 18 | // int arr[N]; 19 | int n; 20 | int c = 0; 21 | void dfs(vector& arr, unordered_map& map, long long num, int i) 22 | { 23 | if (i > arr.size()) //注意,这里是要绝对值的 24 | return; 25 | 26 | map[num] = 1; 27 | if (i == arr.size()) 28 | return; 29 | dfs(arr, map, num + arr[i], i + 1); //放入同一边 30 | 31 | dfs(arr, map, abs(num - arr[i]), i + 1); //放入另外一边 32 | dfs(arr, map, num, i + 1); //不选 33 | } 34 | 35 | int main() 36 | { 37 | cin >> n; 38 | if (n == 1) 39 | { 40 | cout << 1 << endl; 41 | return 0; 42 | } 43 | int res = 0; 44 | vector arr(n); 45 | unordered_map map; 46 | for (int i = 0; i < n; i++) 47 | { 48 | cin >> arr[i]; 49 | } 50 | for (int i = 0; i < arr.size(); i++) 51 | { 52 | dfs(arr, map, arr[i], i + 1); //当前是0 53 | } 54 | for (const auto& m : map) 55 | { 56 | //cout << m.first <<"还是肯定会"<< endl; 57 | if (m.second == 1&&m.first!=0) 58 | res++; 59 | } 60 | cout << res << endl; 61 | 62 | return 0; 63 | } 64 | ``` 65 | ```cpp 66 | //方法二:动态规划,正确做法: 67 | #include 68 | using namespace std; 69 | const long long inf = 0x7fffffffffffffff; 70 | bool dp[106][200006]; 71 | int a[106]; 72 | int main() 73 | { 74 | int i,j,n,sum = 0; cin>>n; 75 | for(i = 1;i <= n;i++){ 76 | cin>>a[i]; 77 | sum+= a[i]; 78 | } 79 | 80 | for(i = 1;i <= n;i++){ 81 | for(j = 1;j <= sum;j++){ 82 | dp[i][j] = dp[i-1][j]; 83 | if(!dp[i][j]){ 84 | if(j == a[i]) dp[i][j] = 1; 85 | //判定的值与当前选择的砝码相等 86 | if(dp[i-1][j + a[i]]) dp[i][j] = 1; 87 | //检查当前i砝码与上一个状态(之前组合出的值)放在不同侧,是否可以组成j。 88 | if(dp[i-1][abs(j-a[i])]) dp[i][j] = 1; 89 | //检查当前i砝码与上一个状态(之前组合出的值)放在同一侧,是否可以组成j。 90 | } 91 | } 92 | } 93 | 94 | long long ans = 0; 95 | for(j = 1;j <= sum;j++) if(dp[n][j]) ans++; 96 | cout< 8 | using namespace std; 9 | int main() { 10 | int n; 11 | cin >> n; 12 | for (int x = 1; x <= 20; x++) { 13 | for (int y = 1; y <= 20; y++) { 14 | for (int i = 1; i <= 20; i++) { 15 | for (int j = 1; j <= 20; j++) { 16 | if ((x * j - y * i) == n) { 17 | cout << x << " " << y << endl; 18 | cout << i << " " << j << endl; 19 | return 0; 20 | } 21 | } 22 | } 23 | } 24 | } 25 | cout << -1 << endl; 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /problems/0112.挑战boss.md: -------------------------------------------------------------------------------- 1 | 2 | # 112. 挑战boss 3 | 4 | 本题题意有点绕,注意看一下 题目描述中的【提示信息】,但是在笔试中,是不给这样的提示信息的。 5 | 6 | 简单模拟: 7 | 8 | ```CPP 9 | #include 10 | using namespace std; 11 | int main() { 12 | int n, a, b, k = 0; 13 | cin >> n >> a >> b; 14 | string s; 15 | cin >> s; 16 | int result = 0; 17 | for (int i = 0; i < s.size(); i++) { 18 | int cur = a + k * b; 19 | result += cur; 20 | ++k; 21 | if (s[i] == 'x') k = 0; 22 | } 23 | cout << result << endl; 24 | return 0; 25 | } 26 | ``` 27 | -------------------------------------------------------------------------------- /problems/0113.国际象棋.md: -------------------------------------------------------------------------------- 1 | 2 | # 113.国际象棋 3 | 4 | 广搜,但本题如果广搜枚举马和象的话会超时。 5 | 6 | 广搜要只枚举马的走位,同时判断是否在对角巷直接走象 7 | 8 | ```CPP 9 | #include 10 | using namespace std; 11 | const int N = 100005, mod = 1000000007; 12 | using ll = long long; 13 | int n, ans; 14 | int dir[][2] = {{1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, -1}, {-2, 1}}; 15 | int main() { 16 | int x1, y1, x2, y2; 17 | cin >> n; 18 | while (n--) { 19 | scanf("%d%d%d%d", &x1, &y1, &x2, &y2); 20 | if (x1 == x2 && y1 == y2) { 21 | cout << 0 << endl; 22 | continue; 23 | } 24 | // 判断象走一步到达 25 | int d = abs(x1 - x2) - abs(y1 - y2); 26 | if (!d) {cout << 1 << endl; continue;} 27 | // 判断马走一步到达 28 | bool one = 0; 29 | for (int i = 0; i < 8; ++i) { 30 | int dx = x1 + dir[i][0], dy = y1 + dir[i][1]; 31 | if (dx == x2 && dy == y2) { 32 | cout << 1 << endl; 33 | one = true; 34 | break; 35 | } 36 | } 37 | if (one) continue; 38 | // 接下来为两步的逻辑, 象走两步或者马走一步,象走一步 39 | // 象直接两步可以到达,这个计算是不是同颜色的格子,象可以在两步到达所有同颜色的格子 40 | int d2 = abs(x1 - x2) + abs(y1 - y2); 41 | if (d2 % 2 == 0) { 42 | cout << 2 << endl; 43 | continue; 44 | } 45 | // 接下来判断马 + 象的组合 46 | bool two = 0; 47 | for (int i = 0; i < 8; ++i) { 48 | int dx = x1 + dir[i][0], dy = y1 + dir[i][1]; 49 | int d = abs(dx - x2) - abs(dy - y2); 50 | if (!d) {cout << 2 << endl; two = true; break;} 51 | } 52 | if (two) continue; 53 | // 剩下的格子全都是三步到达的 54 | cout << 3 << endl; 55 | } 56 | return 0; 57 | } 58 | 59 | ``` 60 | -------------------------------------------------------------------------------- /problems/0114.小欧的平均数.md: -------------------------------------------------------------------------------- 1 | 2 | # 114. 小欧的平均数 3 | 4 | 这道题非常的脑筋急转弯, 读题都要理解半天。 5 | 6 | 初步读题,感觉好像是求 如何最小加减,得到三个数的平均数。 7 | 8 | 但题意不是这样的。 9 | 10 | 小欧的说的三个数平衡,只是三个数里 任何两个数 相加都能被2整除, 那么 也就是说,这三个数 要么都是 奇数,要么都是偶数,才能达到小欧所说的平衡。 11 | 12 | 所以题目要求的,就是,三个数,最小加减1 几次 可以让三个数都变成奇数,或者都变成偶数。 13 | 14 | 所以最终的结果 不是1 就是0,没有其他的。 15 | 16 | 录友可能想,题目出的这么绕干啥? 没办法,企业的笔试题就是这样的。 17 | 18 | ```CPP 19 | #include 20 | #include 21 | using namespace std; 22 | int main() { 23 | int x, y, z; 24 | cin >> x >> y >> z; 25 | int count = (x % 2 == 0) + (y % 2 == 0) + (z % 2 == 0); 26 | cout << min(3 - count, count); 27 | } 28 | ``` 29 | 30 | -------------------------------------------------------------------------------- /problems/0115.组装手机.md: -------------------------------------------------------------------------------- 1 | 2 | # 115. 组装手机 3 | 4 | 这道题是比较难得哈希表题目。 把代码随想录哈希表章节理解透彻,做本题没问题。 5 | 6 | 思路是 7 | 8 | 1. 用哈希表记录 外壳售价 和 手机零件售价 出现的次数 9 | 2. 记录总和出现的次数 10 | 3. 遍历总和,减去 外壳售价,看 手机零件售价出现了几次 11 | 4. 最后累加,取最大值 12 | 13 | 有一个需要注意的点: 数字可以重复,在计算个数的时候,如果计算重复的数字 14 | 15 | 例如 如果输入是 16 | 17 | ``` 18 | 4 19 | 1 1 1 1 20 | 1 1 1 1 21 | ``` 22 | 那么输出应该是 4, 外壳售价 和 手机零件售价 是可以重复的。 23 | 24 | 代码如下: 25 | 26 | ```CPP 27 | #include 28 | #include 29 | #include 30 | #include 31 | using namespace std; 32 | int main() { 33 | int n; 34 | cin >> n; 35 | vector aVec(n, 0); 36 | vector bVec(n, 0); 37 | unordered_map aUmap; 38 | unordered_map bUmap; 39 | for (int i = 0; i < n; i++) { 40 | cin >> aVec[i]; 41 | aUmap[aVec[i]]++; 42 | } 43 | for (int i = 0; i < n; i++) { 44 | cin >> bVec[i]; 45 | bUmap[bVec[i]]++; 46 | } 47 | unordered_set uset; 48 | for (int i = 0; i < n; i++) { 49 | for (int j = 0; j < n; j++){ 50 | uset.insert(aVec[i] + bVec[j]); 51 | } 52 | } 53 | int result = 0; 54 | for (int sum : uset) { 55 | //cout << p.first << endl; 56 | int count = 0; 57 | for (pair p : aUmap) { 58 | //cout << p.first - aVec[i] << endl; 59 | if (sum - p.first > 0 && bUmap[sum - p.first] != 0) { 60 | count += min(bUmap[sum - p.first], p.second); 61 | } 62 | } 63 | result = max(result, count); 64 | } 65 | cout << result << endl; 66 | } 67 | ``` 68 | -------------------------------------------------------------------------------- /problems/0121.大数减法.md: -------------------------------------------------------------------------------- 1 | 2 |

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们受益!

3 | 4 | # 大数减法 5 | 6 | 本题测试数据超过int 和 longlong了,所以考察的使用 string 来模拟 两个大数的 加减操作。 7 | 8 | 当然如果使用python或者Java 使用库函数都可以水过。 9 | 10 | 使用字符串来模拟过程,需要处理以下几个问题: 11 | 12 | * 负号处理:要考虑正负数的处理,如果大数相减的结果是负数,需要在结果前加上负号。 13 | * 大数比较:在进行减法之前,需要确定哪个数大,以便知道结果是否需要添加负号。 14 | * 位数借位:处理大数相减时的借位问题,这类似于手动减法。 15 | 16 | ```CPP 17 | #include 18 | #include 19 | #include 20 | using namespace std; 21 | 22 | // 比较两个字符串表示的数字,返回1表示a > b,0表示a == b,-1表示a < b 23 | int compareStrings(const string& a, const string& b) { 24 | if (a.length() > b.length()) return 1; 25 | if (a.length() < b.length()) return -1; 26 | return a.compare(b); 27 | } 28 | 29 | // 去除字符串左侧的前导零 30 | string removeLeadingZeros(const string& num) { 31 | size_t start = 0; 32 | while (start < num.size() && num[start] == '0') { 33 | start++; 34 | } 35 | return start == num.size() ? "0" : num.substr(start); 36 | } 37 | 38 | // 大数相减,假设a >= b 39 | string subtractStrings(const string& a, const string& b) { 40 | string result; 41 | int len1 = a.length(), len2 = b.length(); 42 | int carry = 0; 43 | 44 | for (int i = 0; i < len1; i++) { 45 | int digitA = a[len1 - 1 - i] - '0'; 46 | int digitB = i < len2 ? b[len2 - 1 - i] - '0' : 0; 47 | 48 | int digit = digitA - digitB - carry; 49 | if (digit < 0) { 50 | digit += 10; 51 | carry = 1; 52 | } else { 53 | carry = 0; 54 | } 55 | 56 | result.push_back(digit + '0'); 57 | } 58 | 59 | // 去除结果中的前导零 60 | reverse(result.begin(), result.end()); 61 | return removeLeadingZeros(result); 62 | } 63 | 64 | string subtractLargeNumbers(const string& num1, const string& num2) { 65 | string a = num1, b = num2; 66 | 67 | // 比较两个数的大小 68 | int cmp = compareStrings(a, b); 69 | 70 | if (cmp == 0) { 71 | return "0"; // 如果两个数相等,结果为0 72 | } else if (cmp < 0) { 73 | // 如果a < b,交换它们并在结果前加上负号 74 | swap(a, b); 75 | return "-" + subtractStrings(a, b); 76 | } else { 77 | return subtractStrings(a, b); 78 | } 79 | } 80 | 81 | int main() { 82 | string num1, num2; 83 | cin >> num1 >> num2; 84 | 85 | string result = subtractLargeNumbers(num1, num2); 86 | cout << result << endl; 87 | 88 | return 0; 89 | } 90 | 91 | ``` 92 | -------------------------------------------------------------------------------- /problems/0121.小红的区间翻转.md: -------------------------------------------------------------------------------- 1 | 2 | # 121. 小红的区间翻转 3 | 4 | 比较暴力的方式,就是直接模拟, 枚举所有 区间,然后检查其翻转的情况。 5 | 6 | 在检查翻转的时候,需要一些代码优化,否则容易超时。 7 | 8 | ```CPP 9 | #include 10 | #include 11 | using namespace std; 12 | 13 | bool canTransform(const vector& a, const vector& b, int left, int right) { 14 | // 提前检查翻转区间的值是否可以匹配 15 | for (int i = left, j = right; i <= right; i++, j--) { 16 | if (a[i] != b[j]) { 17 | return false; 18 | } 19 | } 20 | // 检查翻转区间外的值是否匹配 21 | for (int i = 0; i < left; i++) { 22 | if (a[i] != b[i]) { 23 | return false; 24 | } 25 | } 26 | for (int i = right + 1; i < a.size(); i++) { 27 | if (a[i] != b[i]) { 28 | return false; 29 | } 30 | } 31 | return true; 32 | } 33 | 34 | int main() { 35 | int n; 36 | cin >> n; 37 | 38 | vector a(n); 39 | vector b(n); 40 | 41 | for (int i = 0; i < n; i++) { 42 | cin >> a[i]; 43 | } 44 | 45 | for (int i = 0; i < n; i++) { 46 | cin >> b[i]; 47 | } 48 | 49 | int count = 0; 50 | 51 | // 遍历所有可能的区间 52 | for (int left = 0; left < n; left++) { 53 | for (int right = left; right < n; right++) { 54 | // 检查翻转区间 [left, right] 后,a 是否可以变成 b 55 | if (canTransform(a, b, left, right)) { 56 | count++; 57 | } 58 | } 59 | } 60 | cout << count << endl; 61 | return 0; 62 | } 63 | ``` 64 | 65 | 也可以事先计算好,最长公共前缀,和最长公共后缀。 66 | 67 | 在公共前缀和公共后缀之间的部分进行翻转操作,这样我们可以减少很多不必要的翻转尝试。 68 | 69 | 通过在公共前缀和后缀之间的部分,找到可以通过翻转使得 a 和 b 相等的区间。 70 | 71 | 以下 为评论区 卡码网用户:码鬼的C++代码 72 | 73 | ```CPP 74 | #include 75 | #include 76 | 77 | using namespace std; 78 | 79 | int main() { 80 | int n; 81 | cin >> n; 82 | vector a(n), b(n); 83 | for (int i = 0; i < n; i++) { 84 | cin >> a[i]; 85 | } 86 | for (int i = 0; i < n; i++) { 87 | cin >> b[i]; 88 | } 89 | 90 | vector prefix(n, 0), suffix(n, 0); 91 | 92 | // 计算前缀相等的位置 93 | int p = 0; 94 | while (p < n && a[p] == b[p]) { 95 | prefix[p] = 1; 96 | p++; 97 | } 98 | 99 | // 计算后缀相等的位置 100 | int s = n - 1; 101 | while (s >= 0 && a[s] == b[s]) { 102 | suffix[s] = 1; 103 | s--; 104 | } 105 | 106 | int count = 0; 107 | 108 | // 遍历所有可能的区间 109 | for (int i = 0; i < n - 1; i++) { 110 | for (int j = i + 1; j < n; j++) { 111 | // 判断前缀和后缀是否相等 112 | if ((i == 0 || prefix[i - 1] == 1) && (j == n - 1 || suffix[j + 1] == 1)) { 113 | // 判断翻转后的子数组是否和目标数组相同 114 | bool is_palindrome = true; 115 | for (int k = 0; k <= (j - i) / 2; k++) { 116 | if (a[i + k] != b[j - k]) { 117 | is_palindrome = false; 118 | break; 119 | } 120 | } 121 | if (is_palindrome) { 122 | count++; 123 | } 124 | } 125 | } 126 | } 127 | 128 | cout << count << endl; 129 | 130 | return 0; 131 | } 132 | ``` 133 | -------------------------------------------------------------------------------- /problems/0123.小红的数组构造.md: -------------------------------------------------------------------------------- 1 | 2 | 121. 小红的数组构造 3 | 4 | 本题大家不要想着真去模拟数组的情况,那样就想复杂了。 5 | 6 | 数组只能是:1k、2k、3k ... (n-1)k、nk,这样 总和就是最小的。 7 | 8 | 注意最后的和可能超过int,所以用 long long。 9 | 10 | 代码如下: 11 | 12 | ```CPP 13 | #include 14 | using namespace std; 15 | int main () { 16 | long long result = 0; 17 | int n, k; 18 | cin >> n >> k; 19 | for (int i = 1; i <= n; i++) { 20 | result += i * k; 21 | } 22 | cout << result << endl; 23 | } 24 | ``` 25 | 26 | 优化思路: 27 | 28 | 29 | 由于要计算1到n的整数之和,可以利用等差数列求和公式来优化计算。 30 | 31 | 和公式:1 + 2 + 3 + ... + n = n * (n + 1) / 2 32 | 33 | 因此,总和 result = k * (n * (n + 1) / 2) 34 | 35 | ```CPP 36 | 37 | #include 38 | using namespace std; 39 | 40 | int main() { 41 | long long result = 0; 42 | int n, k; 43 | cin >> n >> k; 44 | 45 | // 使用等差数列求和公式进行计算 46 | result = k * (n * (n + 1LL) / 2); 47 | 48 | cout << result << endl; 49 | return 0; 50 | } 51 | 52 | ``` 53 | -------------------------------------------------------------------------------- /problems/0124.精华帖子.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 122.精华帖子 4 | 5 | 6 | 开辟一个数组,默认都是0,把精华帖标记为1. 7 | 8 | 使用前缀和,快速计算出,k 范围内 有多少个精华帖。 9 | 10 | 前缀和要特别注意区间问题,即 vec[i+k] - vec[i] 求得区间和是 (i, i + k] 这个区间,注意这是一个左开右闭的区间。 11 | 12 | 所以前缀和 很容易漏掉 vec[0] 这个数值的计算 13 | 14 | ```CPP 15 | #include 16 | #include 17 | using namespace std; 18 | int main() { 19 | int n, m, k, l, r; 20 | cin >> n >> m >> k; 21 | vector vec(n); 22 | while (m--) { 23 | cin >> l >> r; 24 | for (int i = l; i < r; i++) vec[i] = 1; 25 | } 26 | int result = 0; 27 | for (int i = 0; i < k; i++) result += vec[i]; // 提前预处理result,包含vec[0]的区间,否则前缀和容易漏掉这个区间 28 | 29 | for (int i = 1; i < n; i++) { 30 | vec[i] += vec[i - 1]; 31 | } 32 | 33 | for (int i = 0; i < n - k; i++) { 34 | result = max (result, vec[i + k] - vec[i]); 35 | } 36 | cout << result << endl; 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /problems/0125.连续子数组最大和.md: -------------------------------------------------------------------------------- 1 | 2 | # 123.连续子数组最大和 3 | 4 | 这道题目可以说是 [代码随想录,动态规划:最大子序和](https://www.programmercarl.com/0053.%E6%9C%80%E5%A4%A7%E5%AD%90%E5%BA%8F%E5%92%8C%EF%BC%88%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%EF%BC%89.html) 的升级版。 5 | 6 | 题目求的是 可以替换一个数字 之后 的 连续子数组最大和。 7 | 8 | 如果替换的是数组下标 i 的元素。 9 | 10 | 那么可以用 [代码随想录,动态规划:最大子序和](https://www.programmercarl.com/0053.%E6%9C%80%E5%A4%A7%E5%AD%90%E5%BA%8F%E5%92%8C%EF%BC%88%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%EF%BC%89.html) 的方法,先求出 [0 - i) 区间的 最大子序和 dp1 和 (i, n)的最大子序和dp2 。 11 | 12 | 然后在遍历一遍i, 计算 dp1 + dp2 + vec[i] 的最大值就可以。 13 | 14 | 正序遍历,求出 [0 - i) 区间的 最大子序,dp[ i - 1] 表示 是 包括下标i - 1(以vec[i - 1]为结尾)的最大连续子序列和为dp[i - 1]。 15 | 16 | 所以 在计算区间 (i, n)即 dp2 的时候,我们要倒叙。 因为我们求的是以 包括下标i + 1 为起始位置的最大连续子序列和为dp[i + 1]。 17 | 18 | 这样 dp1 + dp2 + vec[i] 才是一个完整区间。 19 | 20 | 这里就体现出对 dp数组定义的把控,本题如果对 dp数组含义理解不清,其实是不容易做出来的。 21 | 22 | 代码: 23 | 24 | ```CPP 25 | #include 26 | #include 27 | #include 28 | using namespace std; 29 | int main() { 30 | int t, n, x; 31 | cin >> t; 32 | while (t--) { 33 | cin >> n >> x; 34 | vector vec(n); 35 | for (int i = 0; i < n; i++) cin >> vec[i]; 36 | vector dp1(n); 37 | dp1[0] = vec[0]; 38 | int res = vec[0]; 39 | // 从前向后统计最大子序和 40 | for (int i = 1; i < n; i++) { 41 | dp1[i] = max(dp1[i - 1] + vec[i], vec[i]); // 状态转移公式 42 | res = max(res, dp1[i]); 43 | } 44 | 45 | res = max(res, vec[n - 1]); 46 | // 从后向前统计最大子序和 47 | vector dp2(n); 48 | dp2[n - 1] = vec[n - 1]; 49 | for (int i = n - 2; i >= 0; i--) { 50 | dp2[i] = max(dp2[i + 1] + vec[i], vec[i]); 51 | 52 | } 53 | 54 | for (int i = 0 ; i < n ; i++) { 55 | int dp1res = 0; 56 | if (i > 0) dp1res = max(dp1[i-1], 0); 57 | int dp2res = 0; 58 | if (i < n - 1 ) dp2res = max(dp2[i+1], 0); 59 | 60 | res = max(res, dp1res + dp2res + x); 61 | } 62 | cout << res << endl; 63 | } 64 | 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /problems/0127.小美的排列询问.md: -------------------------------------------------------------------------------- 1 | 2 | # 小美的排列询问 3 | 4 | 模拟题,注意 x 和y 不分先后 5 | 6 | ```CPP 7 | 8 | #include 9 | #include 10 | using namespace std; 11 | int main() { 12 | int n, x, y; 13 | cin >> n; 14 | vector vec(n, 0); 15 | for (int i =0; i < n; i++) { 16 | cin >> vec[i]; 17 | } 18 | cin >> x >> y; 19 | for (int i = 0; i < n - 1; i++) { 20 | if (x == vec[i] && y == vec[i + 1]) || (y == vec[i] && x == vec[i + 1]) ) { 21 | cout << "Yes" << endl; 22 | return 0; 23 | } 24 | } 25 | cout << "No" << endl; 26 | 27 | } 28 | 29 | ``` 30 | -------------------------------------------------------------------------------- /problems/0128.小美走公路.md: -------------------------------------------------------------------------------- 1 | 2 | # 小美走公路 3 | 4 | 在处理环形情况的时候,很多录友容易算懵了,不是多算一个数,就是少算一个数。 5 | 6 | 这里这样的题目,最好的方式是将 两个环展开,首尾相连,这样我们就可以通过 直线的思维去解题了 7 | 8 | 两个注意点: 9 | 10 | 1. x 可以比 y 大,题目没规定 x 和y 的大小顺序 11 | 2. 累计相加的数可能超过int 12 | 13 | 14 | ```CPP 15 | #include 16 | #include 17 | using namespace std; 18 | int main () { 19 | int n; 20 | cin >> n; 21 | vector vec(2* n + 1, 0); 22 | for (int i = 1; i <= n; i++) { 23 | cin >> vec[i]; 24 | vec[n + i] = vec[i]; 25 | } 26 | int x, y; 27 | cin >> x >> y; 28 | int xx = min(x ,y); // 注意点1:x 可以比 y 大 29 | int yy = max(x, y); 30 | long long a = 0, b = 0; // 注意点2:相加的数可能超过int 31 | for (int i = xx; i < yy; i++) a += vec[i]; 32 | for (int i = yy; i < xx + n; i++ ) b += vec[i]; 33 | cout << min(a, b) << endl; 34 | } 35 | ``` 36 | -------------------------------------------------------------------------------- /problems/0129.小美的蛋糕切割.md: -------------------------------------------------------------------------------- 1 | 2 | # 小美的蛋糕切割 3 | 4 | 二维前缀和,不了解前缀和的录友 可以自行查一下,是一个很容易理解的算法思路 5 | 6 | ```CPP 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | int main () { 14 | int n, m; 15 | cin >> n >> m; 16 | int sum = 0; 17 | vector> vec(n, vector(m, 0)) ; 18 | for (int i = 0; i < n; i++) { 19 | for (int j = 0; j < m; j++) { 20 | cin >> vec[i][j]; 21 | sum += vec[i][j]; 22 | } 23 | } 24 | // 统计横向 25 | vector horizontal(n, 0); 26 | for (int i = 0; i < n; i++) { 27 | for (int j = 0 ; j < m; j++) { 28 | horizontal[i] += vec[i][j]; 29 | } 30 | } 31 | // 统计纵向 32 | vector vertical(m , 0); 33 | for (int j = 0; j < m; j++) { 34 | for (int i = 0 ; i < n; i++) { 35 | vertical[j] += vec[i][j]; 36 | } 37 | } 38 | int result = INT_MAX; 39 | int horizontalCut = 0; 40 | for (int i = 0 ; i < n; i++) { 41 | horizontalCut += horizontal[i]; 42 | result = min(result, abs(sum - horizontalCut - horizontalCut)); 43 | } 44 | int verticalCut = 0; 45 | for (int j = 0; j < m; j++) { 46 | verticalCut += vertical[j]; 47 | result = min(result, abs(sum - verticalCut - verticalCut)); 48 | } 49 | cout << result << endl; 50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /problems/0130.小美的字符串变换.md: -------------------------------------------------------------------------------- 1 | 2 | # 130.小美的字符串变换 3 | 4 | 本题是[岛屿数量](./0099.岛屿的数量广搜.md)的进阶版,主要思路和代码都是一样的,统计一个图里岛屿的数量,也是染色问题。 5 | 6 | 1、 先枚举各个可能出现的矩阵 7 | 2、 针对矩阵经行广搜染色(深搜,并查集一样可以) 8 | 3、 统计岛屿数量最小的数量。 9 | 10 | ```CPP 11 | #include 12 | #include 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | // 广搜代码同 卡码网:99. 岛屿数量 18 | int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向 19 | void bfs(const vector>& grid, vector>& visited, int x, int y, char a) { 20 | queue> que; 21 | que.push({x, y}); 22 | visited[x][y] = true; // 只要加入队列,立刻标记 23 | while(!que.empty()) { 24 | pair cur = que.front(); que.pop(); 25 | int curx = cur.first; 26 | int cury = cur.second; 27 | for (int i = 0; i < 4; i++) { 28 | int nextx = curx + dir[i][0]; 29 | int nexty = cury + dir[i][1]; 30 | if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 越界了,直接跳过 31 | if (!visited[nextx][nexty] && grid[nextx][nexty] == a) { 32 | que.push({nextx, nexty}); 33 | visited[nextx][nexty] = true; // 只要加入队列立刻标记 34 | } 35 | } 36 | } 37 | } 38 | 39 | int main() { 40 | int n; 41 | string s; 42 | cin >> n; 43 | int result = INT_MAX; 44 | cin >> s; 45 | for (int k = 1; k < n; k++) { 46 | if (n % k != 0) continue; 47 | // 计算出 矩阵的 行 和 列 48 | int x = n / k; 49 | int y = k; 50 | //cout << x << " " << y << endl; 51 | vector> vec(x, vector(y, 0)); 52 | // 填装矩阵 53 | int sCount = 0; 54 | for (int i = 0; i < x; i++) { 55 | for (int j = 0; j < y; j++) { 56 | vec[i][j] = s[sCount++]; 57 | } 58 | } 59 | 60 | // 开始广搜染色 61 | vector> visited(x, vector(y, false)); 62 | int count = 0; 63 | for (int i = 0; i < x; i++) { 64 | for (int j = 0; j < y; j++) { 65 | 66 | if (!visited[i][j]) { 67 | count++; // 遇到没访问过的陆地,+1 68 | bfs(vec, visited, i, j, vec[i][j]); // 将与其链接的陆地都标记上 true 69 | } 70 | } 71 | } 72 | // 取岛屿数量最少的 73 | result = min (result, count); 74 | 75 | } 76 | cout << result << endl; 77 | } 78 | ``` 79 | -------------------------------------------------------------------------------- /problems/0131.小美的树上染色.md: -------------------------------------------------------------------------------- 1 | # 131. 小美的树上染色 2 | 3 | 本题为树形dp 稍有难度,主要在于 递推公式上。 4 | 5 | dp数组的定义: 6 | 7 | dp[cur][1] :当前节点染色,那么当前节点为根节点及其左右子节点中,可以染色的最大数量 8 | 9 | dp[cur][0] :当前节点不染色,那么当前节点为根节点及其左右子节点中,可以染色的最大数量 10 | 11 | 关于 dp转移方程 12 | 13 | 1、 情况一: 14 | 15 | 如果当前节点不染色,那就去 子节点 染色 或者 不染色的最大值。 16 | 17 | `dp[cur][0] += max(dp[child][0], dp[child][1]);` 18 | 19 | 20 | 2、情况二: 21 | 22 | 那么当前节点染色的话,这种情况就不好想了。 23 | 24 | 首先这不是二叉树,每一个节点都有可能 会有n个子节点。 25 | 26 | 所以我们要分别讨论,每一个子节点的情况 对父节点的影响。 27 | 28 | 那么父节点 针对每种情况,就要去 最大值, 也就是 `dp[cur][1] = max(dp[cur][1], 每个自孩子的情况)` 29 | 30 | 如图,假如节点1 是我们要计算的父节点,节点2是我们这次要计算的子节点。 31 | 32 | ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240617204601.png) 33 | 34 | 选中一个节点2 作为我们这次计算的子节点,父节点染色的话,子节点必染色。 35 | 36 | 接下来就是计算 父节点1和该子节点2染色的话, 以子节点2 为根的 染色节点的最大数量 。 37 | 38 | 是:节点2不染色 且 以节点2为根节点的最大 染色数量 + 2, + 2 是因为 节点 1 和 节点2 要颜色了,染色节点增加两个。 39 | 40 | 代码:`dp[child][0] + 2` 41 | 42 | 细心的录友会发现,那我们只计算了 红色框里面的,那么框外 最大的染色数量是多少呢? 43 | 44 | ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240617205709.png) 45 | 46 | 47 | 先看 作为子节点的节点2 为根节点的最大染色数量是多少? 取一个最值,即 节点2染色 或者 不染色取最大值。 48 | 49 | 代码:`max(dp[child][0], dp[child][1])` 50 | 51 | 那么红框以外的 染色最大节点数量 就是 `dp[cur][0] - max(dp[child][0], dp[child][1])` 52 | 53 | (cur是节点1,child是节点2) 54 | 55 | 红框以外的染色最大数量 + 父节点1和该子节点2染色的话 以子节点2 为根的 染色节点的最大数量 就是 节点1 染色的最大节点数量。 56 | 57 | 代码: 58 | 59 | `dp[cur][1] = max(dp[cur][1], dp[cur][0] - max(dp[child][0], dp[child][1]) + dp[child][0] + 2);` 60 | 61 | 整体代码如下: 62 | 63 | ```CPP 64 | 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | 71 | using namespace std; 72 | 73 | int maxN = 10005; 74 | vector> dp (maxN, vector(2, 0)); 75 | vector> grid(maxN); // 邻接表 76 | vector value(maxN); // 存储每个节点的权值 77 | 78 | 79 | // 在树上进行动态规划的函数 80 | void dpOnTheTree(int cur) { 81 | 82 | for (int child : grid[cur]) { 83 | // 后序遍历,从下向上计算 84 | dpOnTheTree(child); 85 | // 情况一 86 | dp[cur][0] += max(dp[child][0], dp[child][1]); 87 | 88 | } 89 | 90 | // 计算dp[1] - 当前节点染色 91 | for (int child : grid[cur]) { 92 | long mul = value[cur] * value[child]; // 当前节点和相邻节点权值的乘积 93 | long sqrtNum = (long) sqrt(mul); 94 | 95 | if (sqrtNum * sqrtNum == mul) { // 如果乘积是完全平方数 96 | // 情况二 97 | // dp[cur][0] 表示所有子节点 染色或者不染色的 最大染色数量 98 | // max(dp[child][0], dp[child][1]) 需要染色节点的孩子节点的最大染色数量 99 | // dp[cur][0] - max(dp[child][0], dp[child][1]) 除了要染色的节点及其子节点,其他孩子的最大染色数量 100 | // 最后 + dp[child][0] + 2 , 就是本节点染色的最大染色节点数量 101 | dp[cur][1] = max(dp[cur][1], dp[cur][0] - max(dp[child][0], dp[child][1]) + dp[child][0] + 2); 102 | } 103 | } 104 | 105 | } 106 | 107 | int main() { 108 | 109 | int n; 110 | cin >> n; // 输入节点数量 111 | 112 | // 读取节点权值 113 | for (int i = 1; i <= n; ++i) { 114 | cin >> value[i]; 115 | } 116 | 117 | // 构建树的邻接表 118 | for (int i = 1; i < n; ++i) { 119 | int x, y; 120 | cin >> x >> y; 121 | grid[x].push_back(y); 122 | } 123 | 124 | // 从根节点(节点1)开始进行动态规划 125 | dpOnTheTree(1); 126 | 127 | // 输出最大染色节点数量 128 | cout << max(dp[1][0], dp[1][1]) << endl; 129 | 130 | return 0; 131 | } 132 | 133 | ``` 134 | 135 | -------------------------------------------------------------------------------- /problems/0132.夹吃旗.md: -------------------------------------------------------------------------------- 1 | 2 | # 132. 夹吃棋 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1209) 5 | 6 | 这道题是模拟题,但很多录友可能想复杂了。 7 | 8 | 行方向,白棋吃,只有这样的布局 `o*o`,黑棋吃,只有这样的布局 `*o*` 9 | 10 | 列方向也是同理的。 11 | 12 | 想到这一点,本题的代码就容易写了, C++代码如下: 13 | 14 | ```CPP 15 | #include 16 | #include 17 | using namespace std; 18 | int main() { 19 | int n; 20 | cin >> n; 21 | while (n--) { 22 | int black = 0, white = 0; 23 | vector grid(3, ""); 24 | // 判断行 25 | for (int i = 0; i < 3; i++) { 26 | cin >> grid[i]; 27 | if (grid[i] == "o*o") white++; 28 | if (grid[i] == "*o*") black++; 29 | } 30 | // 判断列 31 | for (int i = 0; i < 3; i++) { 32 | string s; 33 | s += grid[0][i]; 34 | s += grid[1][i]; 35 | s += grid[2][i]; 36 | if (s == "o*o") white++; 37 | if (s == "*o*") black++; 38 | } 39 | // 如果一个棋盘的局面没有一方被夹吃或者黑白双方都被对面夹吃,则认为是平局 40 | if ((!white && !black) || (white && black)) cout << "draw" << endl; 41 | // 白棋赢 42 | else if (white && !black) cout << "yukan" << endl; 43 | // 黑棋赢 44 | else cout << "kou" << endl; 45 | } 46 | } 47 | ``` 48 | -------------------------------------------------------------------------------- /problems/0133.小红买药.md: -------------------------------------------------------------------------------- 1 | 2 | # 133. 小红买药 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1210) 5 | 6 | 本题是一道直观的模拟题,但也并不简单,很多情况容易漏了,笔试现场可能要多错几次 才能把情况都想到。 7 | 8 | 主要是三个情况: 9 | 10 | * 小红没症状,药有副作用,统计加一,同时要给小红标记上症状 11 | * 小红有症状,药治不了,同时也没副症状 ,这时也要统计加一 12 | * 小红有症状,药可以治,给小红取消症状标记 13 | 14 | 15 | ```CPP 16 | #include 17 | #include 18 | using namespace std; 19 | int main() { 20 | int n, m, q, u; 21 | cin >> n; 22 | string s; 23 | cin >> s; 24 | cin >> m; 25 | vector a(m + 1); // 因为后面u是从1开始的 26 | vector b(m + 1); 27 | for (int i = 1; i <= m; i++) { 28 | cin >> a[i] >> b[i]; 29 | } 30 | cin >> q; 31 | while (q--) { 32 | cin >> u; 33 | int num = 0; 34 | for (int i = 0; i < n; i++) { 35 | // s 没症状,但b给了副作用,统计num的同时,要给s标记上症状 36 | if (s[i] == '0' && b[u][i] == '1') { 37 | num ++; 38 | s[i] = '1'; 39 | } 40 | // s 有症状,但 a治不了,b也没副症状 41 | else if (s[i] == '1' && a[u][i] == '0' && a[u][i] == '0') num++; 42 | // s 有症状,a 可以治 43 | else if (s[i] == '1' && a[u][i] == '1') s[i] = '0'; 44 | } 45 | cout << num << endl; 46 | } 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /problems/0134.皇后移动的最小步数.md: -------------------------------------------------------------------------------- 1 | 2 | # 134. 皇后移动的最小步数 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1211) 5 | 6 | 本题和 [代码随想录-不同路径](https://www.programmercarl.com/0062.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84.html) 有一些类似。 7 | 8 | 关键是弄清楚递推公式 9 | 10 | 一共分三个情况, 11 | 12 | 情况一,向右移动: 13 | 14 | 然后从 (i, j) 再向右走 到 (i, k)。 无论k 多大,步数只加1 : 15 | 16 | `dp[i][k] = dp[i][j] + 1` 17 | 18 | 那么 `dp[i][k]` 也有可能 从其他方向得到,例如 从上到下, 或者斜上方到达 dp[i][k] 19 | 20 | 本题我们要求最小步数,所以取最小值:`dp[i][k] = min(dp[i][k], dp[i][j] + 1);` 21 | 22 | 情况二,向下移动: 23 | 24 | 从 (i, j) 再向下走 到 (k, j)。 无论k 多大,步数只加1 : 25 | 26 | `dp[k][j] = dp[i][j] + 1;` 27 | 28 | 同理 `dp[i][k]` 也有可能 从其他方向得到,取最小值:`dp[k][j] = min(dp[k][j], dp[i][j] + 1);` 29 | 30 | 情况三,右下方移动: 31 | 32 | 从 (i, j) 再向右下方移动 到 (i + k, j + k)。 无论k 多大,步数只加1 : 33 | 34 | `dp[i + k][j + k] = dp[i][j] + 1` 35 | 36 | 同理 `dp[i + k][j + k]` 也有可能 从其他方向得到,取最小值:`dp[i + k][j + k] = min(dp[i + k][j + k], dp[i][j] + 1);` 37 | 38 | 39 | ```CPP 40 | #include 41 | #include 42 | using namespace std; 43 | const int INF = 4e6; // 最多步数也就是 2000 * 2000 44 | int main() { 45 | int n, m; 46 | cin >> n >> m; 47 | vector> grid(n, vector(m)); 48 | for (int i = 0; i < n; i++) { 49 | for (int j = 0; j < m; j++) { 50 | cin >> grid[i][j]; 51 | } 52 | } 53 | vector> dp(n, vector(m, INF)); 54 | dp[0][0] = 0; 55 | for (int i = 0; i < n; i++) { 56 | for (int j = 0; j < m; j++) { 57 | if (grid[i][j] == '*') continue; 58 | // 向右移动k个格子 59 | for (int k = j + 1; k < m && grid[i][k] == '.'; k++) { 60 | dp[i][k] = min(dp[i][k], dp[i][j] + 1); 61 | } 62 | // 向下移动 k个格子 63 | for (int k = i + 1; k < n && grid[k][j] == '.'; k++) { 64 | dp[k][j] = min(dp[k][j], dp[i][j] + 1); 65 | } 66 | // 向右下移动k个格子 67 | for (int k = 1; i + k < n && j + k < m && grid[i + k][j + k] == '.'; k++) { 68 | dp[i + k][j + k] = min(dp[i + k][j + k], dp[i][j] + 1); 69 | } 70 | } 71 | } 72 | if (dp[n - 1][m - 1] == INF) cout << -1 << endl; 73 | else cout << dp[n - 1][m - 1] << endl; 74 | } 75 | ``` 76 | 77 | 78 | -------------------------------------------------------------------------------- /problems/0139.可爱串.md: -------------------------------------------------------------------------------- 1 | 2 | # 可爱串 3 | 4 | 整体思路,就含有 子序列的字符串数量 减去 含有子串的字符串数量。 5 | 6 | 因为子序列数量已经是包含子串数量的。 剩下的就是 只有子序列 且没有子串的 字符串数量。 7 | 8 | 9 | 需要注意我们求的不是 长度为 i 的字符串里有多少个 red 子序列。 10 | 11 | **而是 可以有多少个 长度为i 的字符串 含有子序列 red** 12 | 13 | 同理,可以有多少个长度为i的字符串含有 red 子串 14 | 15 | 认清这一点很重要! 16 | 17 | ### 求子串 18 | 19 | dp2[i][3] 长度为i 且 含有子串 red 的字符串数量 有多少 20 | 21 | dp2[i][2] 长度为i 且 含有子串 re 的字符串数量有多少 22 | 23 | dp2[i][1] 长度为 i 且 含有子串 r 的字符串数量有多少 24 | 25 | dp2[1][0] 长度为 i 且 含有 只有 de, ee , e, d的字符串的字符串数量有多少。 26 | 27 | ```CPP 28 | // 求子串 29 | dp2[0][0] = 1; 30 | for(int i = 1;i <= n; i++) { 31 | dp2[i][0] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0] * 2) % mod; // 含有 re 的可以把 r改成d, 含有r 的可以改成 32 | dp2[i][1] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0]) % mod; 33 | dp2[i][2] = (dp2[i - 1][1]); 34 | dp2[i][3] = (dp2[i - 1][3] * 3 + dp2[i - 1][2]) % mod; 35 | } 36 | `` 37 | 38 | ### 求子序列 39 | 40 | dp1[i][3] 长度为i 且 含有子序列 red 的字符串数量 有多少 41 | 42 | dp2[i][2] 长度为i 且 含有子序列 re 的字符串数量有多少 43 | 44 | dp2[i][1] 长度为 i 且 含有子序列 r 的字符串数量有多少 45 | 46 | dp2[1][0] 长度为 i 且 含有 只含有 e 和 d 的字符串的字符串数量有多少。 47 | 48 | ```CPP 49 | 50 | // 求子序列 51 | dp1[0][0]=1; 52 | for(int i=1;i<=n;i++) 53 | { 54 | dp1[i][0] = (dp1[i - 1][0] * 2) % mod; 55 | dp1[i][1] = (dp1[i - 1][0] + dp1[i - 1][1] * 2) % mod; 56 | dp1[i][2] = (dp1[i - 1][1] + dp1[i - 1][2] * 2) % mod; 57 | dp1[i][3] = (dp1[i - 1][2] + dp1[i - 1][3] * 3) % mod; 58 | } 59 | ``` 60 | 61 | 62 | 63 | ```CPP 64 | 65 | #include 66 | using namespace std; 67 | 68 | using ll=long long; 69 | const int mod=1e9+7; 70 | 71 | int main() 72 | { 73 | int n; 74 | 75 | cin>>n; 76 | vector> dp1(n + 1,vector (4,0)); 77 | vector> dp2(n + 1,vector (4,0)); 78 | // 求子串 79 | dp2[0][0] = 1; 80 | for(int i = 1;i <= n; i++) { 81 | dp2[i][0] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0] * 2) % mod; 82 | dp2[i][1] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0]) % mod; 83 | dp2[i][2] = (dp2[i - 1][1]); 84 | dp2[i][3] = (dp2[i - 1][3] * 3 + dp2[i - 1][2]) % mod; 85 | } 86 | 87 | // 求子序列 88 | dp1[0][0]=1; 89 | for(int i=1;i<=n;i++) 90 | { 91 | dp1[i][0] = (dp1[i - 1][0] * 2) % mod; 92 | dp1[i][1] = (dp1[i - 1][0] + dp1[i - 1][1] * 2) % mod; 93 | dp1[i][2] = (dp1[i - 1][1] + dp1[i - 1][2] * 2) % mod; 94 | dp1[i][3] = (dp1[i - 1][2] + dp1[i - 1][3] * 3) % mod; 95 | } 96 | 97 | cout<<(dp1[n][3] - dp2[n][3])%mod; 98 | 99 | } 100 | 101 | ``` 102 | -------------------------------------------------------------------------------- /problems/0139.完美数.md: -------------------------------------------------------------------------------- 1 | 2 | ```CPP 3 | #include 4 | #include 5 | using namespace std; 6 | int countOnes(long long num) { 7 | int zeroCount = 0; 8 | while (num > 0) { 9 | if (num % 10 != 0) { // 检查最低位是否为0 10 | zeroCount++; 11 | } 12 | num /= 10; // 移除最低位 13 | } 14 | return zeroCount; 15 | } 16 | int main() { 17 | int n; 18 | cin >> n; 19 | vector vec(n); 20 | for (int i = 0; i < n; i++) cin >> vec[i]; 21 | int result = 0; 22 | for (int i = 0; i < n; i++) { 23 | for (int j = i + 1; j < n; j++) { 24 | if (countOnes(vec[i] * vec[j]) == 1) result++; 25 | } 26 | } 27 | cout << result << endl; 28 | } 29 | ``` 30 | -------------------------------------------------------------------------------- /problems/0141.好二叉树.md: -------------------------------------------------------------------------------- 1 | 2 | 本题和 [96.不同的二叉搜索树](https://www.programmercarl.com/0096.%E4%B8%8D%E5%90%8C%E7%9A%84%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91.html) 比较像 3 | 4 | * 取模这里很容易出错 5 | * 过程中所用到的数值都有可能超过int,所以要改用longlong 6 | 7 | ```CPP 8 | #include 9 | #include 10 | using namespace std; 11 | 12 | long long mod = 1e9 + 7; 13 | long long dp(int t, vector& memory) { 14 | if (t % 2 == 0) return 0; 15 | if (t == 1) return 1; 16 | if (memory[t] != -1) return memory[t]; 17 | 18 | long long result = 0; 19 | // 枚举左右子树节点的数量 20 | for (int i = 1; i < t; i += 2) { 21 | long long leftNum = dp(i, memory); // 左子树节点数量为i 22 | long long rightNum = dp(t - i - 1, memory); // 右子树节点数量为t - i - 1 23 | result += (leftNum * rightNum) % mod; // 注意这里是乘的关系 24 | result %= mod; 25 | } 26 | memory[t] = result; 27 | return result; 28 | } 29 | int main() { 30 | int n; 31 | cin >> n; 32 | vector memory(n + 1, -1); 33 | cout << dp(n, memory) << endl; 34 | } 35 | ``` 36 | 37 | 38 | ```CPP 39 | #include 40 | #include 41 | #include 42 | 43 | using namespace std; 44 | 45 | const int MOD = 1000000007; 46 | 47 | int main() { 48 | int num; 49 | cin >> num; 50 | 51 | if (num % 2 == 0) { 52 | cout << 0 << endl; 53 | return 0; 54 | } 55 | 56 | vector dp(num + 1, 0); 57 | dp[1] = 1; 58 | 59 | for (int i = 3; i <= num; i += 2) { 60 | for (int j = 1; j <= i - 2; j += 2) { 61 | dp[i] = (dp[i] + dp[j] * dp[i - 1 - j]) % MOD; 62 | } 63 | } 64 | 65 | cout << dp[num] << endl; 66 | return 0; 67 | } 68 | 69 | ``` 70 | 71 | 72 | 第二题的代码 73 | 74 | #include 75 | using namespace std; 76 | 77 | long fastexp(long base,long n,long mod){ 78 | long answer = 1; 79 | while(n > 0){ 80 | if(n % 2 == 1){ 81 | answer = (answer * base) % mod; 82 | } 83 | base = (base * base) % mod; 84 | n /= 2; 85 | } 86 | return answer; 87 | } 88 | int kawaiiStrings(int n) { 89 | // write code here 90 | std::vector f(n + 1), g(n + 1), h(n + 1); 91 | long mod = 1000000007; 92 | for (long i = 2; i <= n; i++) g[i] = (g[i - 1] * 2 + (i - 1) * fastexp(2,i-2,mod)) % mod; 93 | for (long i = 3; i <= n; i++) f[i] = ((f[i - 1] * 3) % mod + g[i - 1]) % mod; 94 | for (long i = 3; i <= n; i++) h[i] = (fastexp(3, i - 3, mod) + h[i - 1] * 3 - h[i - 3]) % mod; 95 | return (f[n]-h[n]+mod)%mod; 96 | 97 | } 98 | 99 | int main(){ 100 | int n; 101 | cin >> n; 102 | cout << kawaiiStrings(n) << endl; 103 | return 0; 104 | } 105 | -------------------------------------------------------------------------------- /problems/0144.字典序最小的01字符串.md: -------------------------------------------------------------------------------- 1 | 2 | # 0144.字典序最小的01字符串 3 | 4 | 贪心思路:移动尽可能 移动前面的1 ,这样可以是 字典序最小 5 | 6 | 从前到后遍历,遇到 0 ,就用前面的 1 来交换 7 | 8 | ```CPP 9 | #include 10 | #include 11 | using namespace std; 12 | int main() { 13 | int n,k; 14 | cin >> n >> k; 15 | string s; 16 | cin >> s; 17 | for(int i = 0; i < n && k > 0; i++) { 18 | if(s[i] == '0') { 19 | // 开始用前面的 1 来交换 20 | int j = i; 21 | while(j > 0 && s[j - 1] == '1' && k > 0) { 22 | swap(s[j], s[j - 1]); 23 | --j; 24 | --k; 25 | } 26 | } 27 | } 28 | cout << s << endl; 29 | return 0; 30 | } 31 | 32 | ``` 33 | 34 | Java: 35 | 36 | ```Java 37 | 38 | import java.util.*; 39 | 40 | public class Main { 41 | public static void main(String[] args) { 42 | Scanner scanner = new Scanner(System.in); 43 | int n = scanner.nextInt(); 44 | int k = scanner.nextInt(); 45 | scanner.nextLine(); // 消耗掉换行符 46 | String s = scanner.nextLine(); 47 | char[] ch = s.toCharArray(); 48 | 49 | for (int i = 0; i < n && k > 0; i++) { 50 | if (ch[i] == '0') { 51 | // 开始用前面的 1 来交换 52 | int j = i; 53 | while (j > 0 && ch[j - 1] == '1' && k > 0) { 54 | char tmp = ch[j]; 55 | ch[j] = ch[j - 1]; 56 | ch[j - 1] = tmp; 57 | j--; 58 | k--; 59 | } 60 | } 61 | } 62 | 63 | System.out.println(new String(ch)); 64 | } 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /problems/0145.数组子序列的排列.md: -------------------------------------------------------------------------------- 1 | 2 | # 145. 数组子序列的排列 3 | 4 | 每个元素出现的次数相乘就可以了。 5 | 6 | 注意 “长度为 m 的数组,1 到 m 每个元素都出现过,且恰好出现 1 次。” ,题目中有n个元素,所以我们要统计的就是 1 到 n 元素出现的个数。 7 | 8 | 因为如果有一个元素x 大于n了, 那不可能出现 长度为x的数组 且 1 到 x 每个元素都出现过。 9 | 10 | ```CPP 11 | #include "bits/stdc++.h" 12 | using namespace std; 13 | int main(){ 14 | int n; 15 | int x; 16 | cin >> n; 17 | unordered_map umap; 18 | for(int i = 0; i < n; ++i){ 19 | cin >> x; 20 | if(umap.find(x) != umap.end()) umap[x]++; 21 | else umap[x] = 1; 22 | } 23 | long long res = 0; 24 | long long num = 1; 25 | for (int i = 1; i <= n; i++) { 26 | if (umap.find(i) == umap.end()) break; // 如果i都没出现,后面得数也不能 1 到 m 每个元素都出现过 27 | num = (num * umap[i]) % 1000000007; 28 | res += num; 29 | res %= 1000000007; 30 | } 31 | cout << res << endl; 32 | } 33 | 34 | ``` 35 | 36 | ```Java 37 | 38 | import java.util.HashMap; 39 | import java.util.Map; 40 | import java.util.Scanner; 41 | 42 | public class Main { 43 | public static void main(String[] args) { 44 | Scanner sc = new Scanner(System.in); 45 | int n = sc.nextInt(); 46 | Map map = new HashMap<>(); 47 | for (int i = 0; i < n; i++) { 48 | int x = sc.nextInt(); 49 | map.put(x, map.getOrDefault(x, 0) + 1); 50 | } 51 | long res = 0; 52 | long num = 1; 53 | for (int i = 1; i <= n; i++) { 54 | if (!map.containsKey(i)) break; // 如果i都没出现,后面得数也不能1到m每个元素都出现过 55 | num = (num * map.get(i)) % 1000000007; 56 | res += num; 57 | res %= 1000000007; 58 | } 59 | System.out.println(res); 60 | sc.close(); 61 | } 62 | } 63 | 64 | ``` 65 | 66 | 67 | ```python 68 | def main(): 69 | import sys 70 | input = sys.stdin.read 71 | data = input().split() 72 | 73 | n = int(data[0]) 74 | umap = {} 75 | 76 | for i in range(1, n + 1): 77 | x = int(data[i]) 78 | if x in umap: 79 | umap[x] += 1 80 | else: 81 | umap[x] = 1 82 | 83 | res = 0 84 | num = 1 85 | MOD = 1000000007 86 | 87 | for i in range(1, n + 1): 88 | if i not in umap: 89 | break # 如果i都没出现,后面得数也不能1到m每个元素都出现过 90 | num = (num * umap[i]) % MOD 91 | res = (res + num) % MOD 92 | 93 | print(res) 94 | 95 | if __name__ == "__main__": 96 | main() 97 | 98 | ``` 99 | -------------------------------------------------------------------------------- /problems/0146.传送树.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | # 146. 传送树 6 | 7 | 本题题意是比较绕的,我后面给补上了 【提示信息】对 题目输出样例讲解一下,相对会容易理解的多。 8 | 9 | ```CPP 10 | #include 11 | #include 12 | #include 13 | using namespace std; 14 | 15 | vector> edge; // 邻接表来存图 16 | vector nxt; 17 | int n; 18 | 19 | /* 20 | * 递归函数,用于找到每个节点的下一个传送门节点,并记录在nxt数组中。 21 | * 遍历当前节点的所有子节点,递归调用findNext以确保子节点的nxt值已经计算出来。 22 | * 更新当前节点的nxt值为其子节点中编号最小的节点。 23 | * 如果当前节点是叶子节点(即没有子节点),则将其nxt值设置为自身。 24 | */ 25 | void findNext(int node) { 26 | for (int v : edge[node]) { 27 | findNext(v); 28 | if (nxt[node] == -1 || nxt[node] > min(v, nxt[v])) { 29 | nxt[node] = min(v, nxt[v]); 30 | } 31 | } 32 | 33 | // 叶子节点 34 | if (nxt[node] == -1) { 35 | nxt[node] = node; 36 | } 37 | } 38 | 39 | // 计算从节点u出发经过若干次传送门到达叶子节点所需的步数。 40 | // 通过不断访问nxt节点,直到到达叶子节点,记录访问的节点数。 41 | int get(int u) { 42 | int cnt = 1; 43 | while (nxt[u] != u) { 44 | cnt++; 45 | u = nxt[u]; 46 | } 47 | return cnt; 48 | } 49 | 50 | int main() { 51 | cin >> n; 52 | edge.resize(n + 1); 53 | nxt.resize(n + 1, -1); 54 | for (int i = 1; i <= n; ++i) { 55 | int a, b; 56 | cin >> a >> b; 57 | edge[a].push_back(b); 58 | } 59 | findNext(1); 60 | for (int i = 1; i <= n; ++i) { 61 | cout << get(i) << ' '; 62 | } 63 | } 64 | 65 | ``` 66 | -------------------------------------------------------------------------------- /problems/0147.三珠互斥.md: -------------------------------------------------------------------------------- 1 | 2 | # 三珠互斥 3 | 4 | 1. 如果k * 3 大于 n 了,那说明一定没结果,如果没想明白,大家举个例子试试看 5 | 2. 分别求出三个红珠子之间的距离 6 | 3. 对这三段距离从小到大排序 y1, y2, y3 7 | 4. 如果第一段距离y1 小于k,说明需要交换 k - y 次, 同理 第二段距离y2 小于k,说明需要交换 k - y2 次 8 | 5. y1 y2 都调整好了,不用计算y3,因为 y3是距离最大 9 | 10 | ```CPP 11 | #include 12 | using namespace std; 13 | 14 | int main(){ 15 | int t; 16 | cin >> t; 17 | int n, k, a1, a2, a3; 18 | vector dis(3); 19 | 20 | while (t--) { 21 | cin >> n >> k >> a1 >> a2 >> a3; 22 | if(k * 3 > n){ 23 | cout << -1 << endl; 24 | continue; 25 | } 26 | dis[0] = min(abs(a1 - a2), n - abs(a1 - a2)); 27 | dis[1] = min(abs(a1 - a3), n - abs(a1 - a3)); 28 | dis[2] = min(abs(a3 - a2), n - abs(a3 - a2)); 29 | 30 | sort(dis.begin(), dis.end()); 31 | 32 | int result = 0; 33 | if (dis[0] < k) result += (k - dis[0]); 34 | if (dis[1] < k) result += (k - dis[1]); 35 | 36 | cout << result << endl; 37 | } 38 | return 0; 39 | } 40 | ``` 41 | 42 | Java代码: 43 | 44 | ```Java 45 | import java.util.*; 46 | 47 | public class Main { 48 | public static void main(String[] args) { 49 | Scanner scanner = new Scanner(System.in); 50 | int t = scanner.nextInt(); 51 | 52 | while (t-- > 0) { 53 | int n = scanner.nextInt(); 54 | int k = scanner.nextInt(); 55 | int a1 = scanner.nextInt(); 56 | int a2 = scanner.nextInt(); 57 | int a3 = scanner.nextInt(); 58 | if (k * 3 > n) { 59 | System.out.println(-1); 60 | continue; 61 | } 62 | 63 | List dis = new ArrayList<>(3); 64 | dis.add(Math.min(Math.abs(a1 - a2), n - Math.abs(a1 - a2))); 65 | dis.add(Math.min(Math.abs(a1 - a3), n - Math.abs(a1 - a3))); 66 | dis.add(Math.min(Math.abs(a3 - a2), n - Math.abs(a3 - a2))); 67 | 68 | Collections.sort(dis); 69 | 70 | int result = 0; 71 | if (dis.get(0) < k) result += (k - dis.get(0)); 72 | if (dis.get(1) < k) result += (k - dis.get(1)); 73 | 74 | System.out.println(result); 75 | } 76 | } 77 | } 78 | ``` 79 | -------------------------------------------------------------------------------- /problems/0149.好数组.md: -------------------------------------------------------------------------------- 1 | 2 | # 149. 好数组 3 | 4 | 贪心思路: 5 | 6 | 整体思路是移动到中间位置(中位数),一定是 移动次数最小的。 7 | 8 | 有一个数可以不改变,对数组排序之后, 最小数 和 最大数 一定是移动次数最多的,所以分别保留最小 和 最大的不变。 9 | 10 | 中间可能有两个位置,所以要计算中间偏前 和 中间偏后的 11 | 12 | 代码如下: 13 | 14 | ```CPP 15 | #include 16 | using namespace std; 17 | 18 | int main() { 19 | int n; 20 | cin >> n; 21 | vector arr(n); 22 | for (int i = 0; i < n; ++i) { 23 | cin >> arr[i]; 24 | } 25 | sort(arr.begin(), arr.end()); 26 | 27 | if (arr[0] == arr[n - 1]) { 28 | cout << 1 << endl; 29 | return 0; 30 | } 31 | long cnt = 0L; 32 | long cnt1 = 0L; 33 | 34 | // 如果要保留一个不改变,要不不改最小的,要不不改最大的。 35 | 36 | // 取中间偏前的位置 37 | long mid = arr[(n - 2) / 2]; 38 | 39 | // 不改最大的 40 | for (int i = 0; i < n - 1; i++) { 41 | cnt += abs(arr[i] - mid); 42 | } 43 | 44 | // 取中间偏后的位置 45 | mid = arr[n / 2]; 46 | 47 | // 不改最小的 48 | for (int i = 1; i < n; i++) { 49 | cnt1 += abs(arr[i] - mid); 50 | } 51 | 52 | cout << min(cnt, cnt1) << endl; 53 | return 0; 54 | } 55 | ``` 56 | 57 | Java代码如下: 58 | 59 | ```Java 60 | 61 | import java.util.*; 62 | 63 | public class Main { 64 | public static void main(String[] args) { 65 | Scanner scanner = new Scanner(System.in); 66 | int n = scanner.nextInt(); 67 | long[] arr = new long[n]; 68 | for (int i = 0; i < n; ++i) { 69 | arr[i] = scanner.nextLong(); 70 | } 71 | Arrays.sort(arr); 72 | 73 | if (arr[0] == arr[n - 1]) { 74 | System.out.println(1); 75 | return; 76 | } 77 | long cnt = 0L; 78 | long cnt1 = 0L; 79 | 80 | // 如果要保留一个不改变,要不不改最小的,要不不改最大的。 81 | 82 | // 取中间偏前的位置 83 | long mid = arr[(n - 2) / 2]; 84 | 85 | // 不改最大的 86 | for (int i = 0; i < n - 1; i++) { 87 | cnt += Math.abs(arr[i] - mid); 88 | } 89 | 90 | // 取中间偏后的位置 91 | mid = arr[n / 2]; 92 | 93 | // 不改最小的 94 | for (int i = 1; i < n; i++) { 95 | cnt1 += Math.abs(arr[i] - mid); 96 | } 97 | 98 | System.out.println(Math.min(cnt, cnt1)); 99 | } 100 | } 101 | 102 | ``` 103 | -------------------------------------------------------------------------------- /problems/0150.极长连续段的权值.md: -------------------------------------------------------------------------------- 1 | 2 | # 150. 极长连续段的权值 3 | 4 | 动态规划,枚举最后边节点的情况: 5 | 6 | ```CPP 7 | #include 8 | #include 9 | using namespace std; 10 | 11 | int main() { 12 | int n; 13 | cin >> n; 14 | string s; 15 | cin >> s; 16 | 17 | long long result = 1; 18 | long long a = 1; 19 | 20 | for (int i = 1; i < n; ++i) { 21 | // 加上本身长度为1的子串 22 | if (s[i] == s[i - 1]) { 23 | a += 1; 24 | result += a; 25 | // 以最右节点为终点,每个子串的级长连续段都+1,再加本身长度为1的子串 26 | } else { 27 | a = a + i + 1; 28 | result += a; 29 | } 30 | } 31 | cout << result << endl; 32 | return 0; 33 | } 34 | ``` 35 | 36 | Java代码如下: 37 | 38 | ```Java 39 | import java.util.Scanner; 40 | 41 | public class Main { 42 | public static void main(String[] args) { 43 | Scanner scanner = new Scanner(System.in); 44 | int n = scanner.nextInt(); 45 | String s = scanner.next(); 46 | 47 | long result = 1; 48 | long a = 1; 49 | 50 | for (int i = 1; i < n; ++i) { 51 | // 加上本身长度为1的子串 52 | if (s.charAt(i) == s.charAt(i - 1)) { 53 | a += 1; 54 | result += a; 55 | // 以最右节点为终点,每个子串的级长连续段都+1,再加本身长度为1的子串 56 | } else { 57 | a = a + i + 1; 58 | result += a; 59 | } 60 | } 61 | 62 | System.out.println(result); 63 | } 64 | } 65 | 66 | ``` 67 | -------------------------------------------------------------------------------- /problems/0151.手机流畅运行的秘密.md: -------------------------------------------------------------------------------- 1 | # 151. 手机流畅运行的秘密 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1229) 4 | 5 | 先运行 能留下电量多的 任务,才能有余电运行其他任务。 6 | 7 | 任务1,1:10 ,运行完 能留下 9个电 8 | 9 | 任务2,2:12,运行完 能留下 10个电 10 | 11 | 任务3,3:10,运行完 能留下 7个电。 12 | 13 | 运行顺序: 任务2 -> 任务1 -> 任务3 14 | 15 | 按照 最低初始电量 - 耗电量,从大到小排序。 16 | 17 | 计算总电量,需要 从小到大 遍历, 不断取 总电量 + 任务耗电量 与 任务最低初始电量 的最大值。 18 | 19 | ```CPP 20 | #include 21 | using namespace std; 22 | 23 | bool cmp(const pair& taskA, const pair& taskB) { 24 | return (taskA.second - taskA.first) < (taskB.second - taskB.first); 25 | } 26 | int main() { 27 | string str, tmp; 28 | vector> tasks; 29 | 30 | //处理输入 31 | getline(cin, str); 32 | stringstream ss(str); 33 | while (getline(ss, tmp, ',')) { 34 | int p = tmp.find(":"); 35 | string a = tmp.substr(0, p); 36 | string b = tmp.substr(p + 1); 37 | tasks.push_back({stoi(a), stoi(b)}); 38 | } 39 | 40 | // 按照差值从小到大排序 41 | sort(tasks.begin(), tasks.end(), cmp); 42 | 43 | // 收集结果 44 | int result = 0; 45 | for (int i = 0 ; i < tasks.size(); i++) { 46 | result = max(result + tasks[i].first, tasks[i].second); 47 | } 48 | 49 | result = result <= 4800 ? result : -1; 50 | cout << result << endl; 51 | 52 | } 53 | ``` 54 | 55 | Java版本: 56 | 57 | ```Java 58 | import java.util.*; 59 | import java.util.stream.Collectors; 60 | 61 | public class Main { 62 | public static void main(String[] args) { 63 | Scanner sc = new Scanner(System.in); 64 | String str = sc.nextLine(); 65 | String[] tasksArray = str.split(","); 66 | List tasks = Arrays.stream(tasksArray) 67 | .map(task -> { 68 | String[] parts = task.split(":"); 69 | return new Pair(Integer.parseInt(parts[0]), Integer.parseInt(parts[1])); 70 | }) 71 | .collect(Collectors.toList()); 72 | 73 | // 按照差值从小到大排序 74 | Collections.sort(tasks, (taskA, taskB) -> 75 | (taskA.second - taskA.first) - (taskB.second - taskB.first) 76 | ); 77 | 78 | // 收集结果 79 | int result = 0; 80 | for (Pair task : tasks) { 81 | result = Math.max(result + task.first, task.second); 82 | } 83 | 84 | result = result <= 4800 ? result : -1; 85 | System.out.println(result); 86 | } 87 | } 88 | 89 | class Pair { 90 | int first; 91 | int second; 92 | 93 | Pair(int first, int second) { 94 | this.first = first; 95 | this.second = second; 96 | } 97 | } 98 | 99 | ``` 100 | 101 | Python版本: 102 | 103 | ```python 104 | def main(): 105 | import sys 106 | input = sys.stdin.read 107 | 108 | str = input().strip() 109 | tasks = [] 110 | for tmp in str.split(','): 111 | a, b = map(int, tmp.split(':')) 112 | tasks.append((a, b)) 113 | 114 | # 按照差值从小到大排序 115 | tasks.sort(key=lambda task: task[1] - task[0]) 116 | 117 | # 收集结果 118 | result = 0 119 | for task in tasks: 120 | result = max(result + task[0], task[1]) 121 | 122 | result = result if result <= 4800 else -1 123 | print(result) 124 | 125 | if __name__ == "__main__": 126 | main() 127 | ``` 128 | -------------------------------------------------------------------------------- /problems/0152.小米手机通信校准.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 152. 小米手机通信校准 4 | 5 | [题目链接](https://kamacoder.com/problempage.php?pid=1230) 6 | 7 | 一道模拟题,但比较考察 代码能力。 8 | 9 | 遍历去找 里 freq 最近的 freg就好, 需要记录刚遍历过的的freg和 loss,因为可能有 相邻一样的 freg。 10 | 11 | ```CPP 12 | #include 13 | using namespace std; 14 | 15 | int main() { 16 | int freq; 17 | cin >> freq; 18 | string data; 19 | double result = 0; 20 | int last_freg = 0; // 记录上一个 freg 21 | int last_loss = 0; // 记录上一个loss 22 | while(cin >> data) { 23 | int index = data.find(':'); 24 | int freg = stoi(data.substr(0, index)); // 获取 freg 和 loss 25 | int loss = stoi(data.substr(index + 1)); 26 | // 两遍一样 27 | if(abs(freg - freq) == abs(last_freg - freq)) { 28 | result = (double)(last_loss + loss)/2.0; 29 | } // 否则更新最新的result 30 | else if(abs(freg - freq) < abs(last_freg - freq)){ 31 | result = (double)loss; 32 | } 33 | last_freg = freg; 34 | last_loss = loss; 35 | } 36 | printf("%.1lf\n", result); 37 | return 0; 38 | } 39 | 40 | ``` 41 | 42 | Java 版本: 43 | 44 | ```Java 45 | 46 | import java.util.Scanner; 47 | 48 | public class Main { 49 | public static void main(String[] args) { 50 | Scanner sc = new Scanner(System.in); 51 | int freq = sc.nextInt(); 52 | sc.nextLine(); // 读取换行符 53 | 54 | String inputLine = sc.nextLine(); // 读取包含所有后续输入的行 55 | String[] data = inputLine.split(" "); // 根据空格分割输入 56 | 57 | double result = 0; 58 | int lastFreq = 0; // 记录上一个 freg 59 | int lastLoss = 0; // 记录上一个 loss 60 | 61 | for (String entry : data) { 62 | int index = entry.indexOf(':'); 63 | int freg = Integer.parseInt(entry.substring(0, index)); // 获取 freg 和 loss 64 | int loss = Integer.parseInt(entry.substring(index + 1)); 65 | 66 | // 两遍一样 67 | if (Math.abs(freg - freq) == Math.abs(lastFreq - freq)) { 68 | result = (double) (lastLoss + loss) / 2.0; 69 | } 70 | // 否则更新最新的 result 71 | else if (Math.abs(freg - freq) < Math.abs(lastFreq - freq)) { 72 | result = (double) loss; 73 | } 74 | 75 | lastFreq = freg; 76 | lastLoss = loss; 77 | } 78 | 79 | System.out.printf("%.1f\n", result); 80 | sc.close(); 81 | } 82 | } 83 | 84 | ``` 85 | 86 | Python版本: 87 | 88 | ```python 89 | def main(): 90 | import sys 91 | input = sys.stdin.read 92 | data = input().split() 93 | 94 | freq = int(data[0]) 95 | result = 0 96 | last_freg = 0 # 记录上一个 freg 97 | last_loss = 0 # 记录上一个 loss 98 | 99 | for i in range(1, len(data)): 100 | item = data[i] 101 | index = item.find(':') 102 | freg = int(item[:index]) # 获取 freg 和 loss 103 | loss = int(item[index + 1:]) 104 | 105 | # 两遍一样 106 | if abs(freg - freq) == abs(last_freg - freq): 107 | result = (last_loss + loss) / 2.0 108 | # 否则更新最新的 result 109 | elif abs(freg - freq) < abs(last_freg - freq): 110 | result = loss 111 | 112 | last_freg = freg 113 | last_loss = loss 114 | 115 | print(f"{result:.1f}") 116 | 117 | if __name__ == "__main__": 118 | main() 119 | 120 | 121 | ``` 122 | -------------------------------------------------------------------------------- /problems/0153.权值优势路径计数.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 权值优势路径计数 4 | 5 | [题目链接](https://kamacoder.com/problempage.php?pid=1231) 6 | 7 | 1、构建二叉树:首先根据层序遍历的序列构建二叉树。这可以通过使用队列来实现,队列中存储当前节点及其索引,确保可以正确地将子节点添加到父节点下。 8 | 9 | 2、路径遍历:使用深度优先搜索(DFS)遍历所有从根到叶子的路径。在遍历过程中,维护一个计数器跟踪当前路径中权值为 1 和权值为 0 的节点的数量。 10 | 11 | 3、计数满足条件的路径:每当到达一个叶子节点时,检查当前路径的权值 1 的节点数量是否比权值 0 的节点数量多 1。如果满足,递增一个全局计数器。 12 | 13 | 14 | ```CPP 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | using namespace std; 21 | 22 | struct TreeNode { 23 | int val; 24 | TreeNode *left, *right; 25 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 26 | }; 27 | 28 | // DFS遍历二叉树,并计算满足条件的路径数量 29 | void countPaths(TreeNode* node, int count1, int count0, int& result) { 30 | if (!node) return; 31 | 32 | // 更新当前路径中1和0的数量 33 | node->val == 1 ? count1++ : count0++; 34 | 35 | // 检查当前节点是否为叶子节点 36 | if (!node->left && !node->right) { 37 | // 检查1的数量是否比0的数量多1 38 | if (count1 == count0 + 1) { 39 | result++; 40 | } 41 | return; 42 | } 43 | 44 | // 递归访问左右子节点 45 | countPaths(node->left, count1, count0, result); 46 | countPaths(node->right, count1, count0, result); 47 | } 48 | 49 | int main() { 50 | int N; 51 | cin >> N; 52 | 53 | vector nums(N); 54 | for (int i = 0; i < N; ++i) { 55 | cin >> nums[i]; 56 | } 57 | 58 | if (nums.empty()) { 59 | cout << 0 << endl; 60 | return 0; 61 | } 62 | 63 | // 根据层序遍历的输入构建二叉树 64 | queue q; 65 | TreeNode* root = new TreeNode(nums[0]); 66 | q.push(root); 67 | int index = 1; 68 | 69 | while (!q.empty() && index < N) { 70 | TreeNode* node = q.front(); 71 | q.pop(); 72 | 73 | if (index < N && nums[index] != -1) { 74 | node->left = new TreeNode(nums[index]); 75 | q.push(node->left); 76 | } 77 | index++; 78 | 79 | if (index < N && nums[index] != -1) { 80 | node->right = new TreeNode(nums[index]); 81 | q.push(node->right); 82 | } 83 | index++; 84 | } 85 | 86 | // 计算满足条件的路径数 87 | int result = 0; 88 | countPaths(root, 0, 0, result); 89 | 90 | cout << result << endl; 91 | 92 | return 0; 93 | } 94 | 95 | ``` 96 | -------------------------------------------------------------------------------- /problems/0154.序列中位数.md: -------------------------------------------------------------------------------- 1 | 2 | # 序列中位数 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1232) 5 | 6 | 注意给的数组默认不是有序的! 7 | 8 | 模拟题,排序之后,取中位数,然后按照b数组 删 a数组中元素,再取中位数。 9 | 10 | ```CPP 11 | #include 12 | using namespace std; 13 | 14 | // 计算并返回中位数 15 | double findMedian(vector& nums) { 16 | int n = nums.size(); 17 | if (n % 2 == 1) { 18 | return nums[n / 2]; // 奇数长度,返回中间的元素 19 | } else { 20 | // 偶数长度,返回中间两个元素的平均值 21 | return (nums[n / 2] + nums[n / 2 - 1]) / 2.0; 22 | } 23 | } 24 | 25 | 26 | int main(){ 27 | int t; 28 | cin >> t; 29 | while(t--){ 30 | int n; 31 | cin>> n; 32 | vector a(n); 33 | vector b(n - 1); 34 | for(int i = 0; i < n; i++){ 35 | cin >> a[i]; 36 | } 37 | for(int i = 0; i < n - 1; i++){ 38 | cin >> b[i]; 39 | } 40 | vector nums = a; 41 | vector answers; 42 | 43 | sort(nums.begin(), nums.end()); 44 | 45 | // 把中位数放进结果集 46 | answers.push_back(findMedian(nums)); 47 | 48 | for(int i = 0; i < n - 1; i++){ 49 | 50 | int target = a[b[i]]; 51 | // 删除目标值 52 | nums.erase(find(nums.begin(), nums.end(), target)); 53 | // 把中位数放进结果集 54 | answers.push_back(findMedian(nums)); 55 | 56 | } 57 | 58 | for(auto answer : answers){ 59 | // 判断是否是整数 60 | if(answer == (int)answer) printf("%d ", (int)answer); 61 | else printf("%.1f ", answer); 62 | } 63 | cout << endl; 64 | } 65 | 66 | } 67 | 68 | ``` 69 | -------------------------------------------------------------------------------- /problems/0155.最小化频率的删除代价.md: -------------------------------------------------------------------------------- 1 | 2 | # 最小化频率的删除代价 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1233) 5 | 6 | 计数和排序: 7 | 8 | * 使用 map 或 unordered_map 对数组 a 中每个元素出现的次数进行统计。 9 | * 将统计结果存入一个 vector>,其中 pair 的第一个元素是元素的出现次数,第二个元素是元素本身。 10 | * 按出现次数从大到小排序这个 vector。 11 | 12 | 确定最小 f(a): 13 | 14 | * 从最大出现次数开始尝试减少 f(a)。为此,从最高频次的元素开始逐步向下考虑较少出现的元素,计算达到更低 f(a) 所需删除的元素数量。 15 | * 使用一个累加器 count 来记录需要删除的元素数量,直到这个数量超过允许的最大删除数量 k 或恰好等于 k。在此过程中,尽量使 f(a) 达到最小。 16 | 17 | 计算达到 f(a) 的代价: 18 | 19 | * 计算完成后,需要确定达到最小 f(a) 的确切代价。首先,为每个元素确定在不超过 k 的前提下可以删除的最大数量,以使得 f(a) 最小。 20 | * 对于每个元素,如果它的数量超过了新的 f(a),则计算减少到 f(a) 所需删除的具体元素数,记录下来。 21 | 22 | 计算具体删除代价: 23 | 24 | * 遍历原数组,对于每个需要删除的元素,根据其位置累加删除代价。每删除一个元素,相应地减少其在删除列表中的计数。当某元素需要删除的数量减至 0 时,从删除列表中移除该元素。 25 | 26 | 27 | ```CPP 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | using namespace std; 35 | 36 | int main() { 37 | int n, k; 38 | cin >> n >> k; 39 | 40 | vector a(n); 41 | for (int i = 0; i < n; ++i) { 42 | cin >> a[i]; 43 | } 44 | 45 | unordered_map umap; // 使用map来统计每个元素的出现频率 46 | for (int i = 0; i < n; ++i) { 47 | umap[a[i]]++; // 统计每个元素的出现次数 48 | } 49 | 50 | vector> table; 51 | for (auto& pair : umap) { 52 | table.push_back({pair.second, pair.first}); // 将元素和其频率作为一个pair放入table中 53 | } 54 | 55 | sort(table.begin(), table.end(), greater<>()); // 将table按照频率从大到小排序 56 | 57 | int count = 0; // 用来计算已经删除的元素总数 58 | int minVal = table[0].first; // 从最高频率开始 59 | for (int i = 0; i < table.size(); ++i) { 60 | int freq = table[i].first; 61 | count += (minVal - freq) * i; // 累加删除元素的代价 62 | if (count > k) break; // 如果超过了k,停止循环 63 | else if (count == k) { 64 | minVal = freq; 65 | break; 66 | } else minVal = freq; 67 | } 68 | if (count < k) { 69 | int addDel = (k - count) / table.size(); // 如果删除的代价还没达到k,计算还可以进一步减少的频率 70 | minVal -= addDel; // 减少相应的频率 71 | } 72 | 73 | if (minVal < 0) { 74 | minVal = 0; // 确保最小频率值不小于0 75 | } 76 | 77 | unordered_map deleteList; // 用来存储需要删除的元素及其数量 78 | for (auto& elem : table) { 79 | int num = elem.first; 80 | int ind = elem.second; 81 | if (num > minVal) { 82 | deleteList[ind] = num - minVal; // 如果元素频率大于最小值,计算需要删除的数量 83 | } else { 84 | break; 85 | } 86 | } 87 | 88 | int cost = 0; // 计算总的删除代价 89 | for (int i = 0; i < n; ++i) { 90 | if (deleteList.find(a[i]) != deleteList.end()) { 91 | cost += i + 1; // 删除的代价是元素的索引+1 92 | deleteList[a[i]]--; // 删除一个元素 93 | if (deleteList[a[i]] == 0) { 94 | deleteList.erase(a[i]); // 如果元素已经全部删除,从列表中移除 95 | if (deleteList.empty()) { 96 | break; // 如果没有元素需要删除了,结束循环 97 | } 98 | } 99 | } 100 | } 101 | 102 | cout << minVal << " " << cost << endl; 103 | return 0; 104 | } 105 | 106 | ``` 107 | -------------------------------------------------------------------------------- /problems/0156.勇敢牛牛战斗序列.md: -------------------------------------------------------------------------------- 1 | 2 | # 勇敢牛牛战斗序列 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1234) 5 | 6 | 贪心思路,对数组从小到大排序之后,先取最右边,再取最左边,循环反复。 7 | 8 | ```CPP 9 | #include 10 | 11 | using namespace std; 12 | 13 | int main() { 14 | int n; 15 | cin >> n; 16 | vector a(n); // 使用 vector 存储整数数组 17 | for (int i = 0; i < n; i++) { 18 | cin >> a[i]; // 读取数组 19 | } 20 | sort(a.begin(), a.end()); // 对数组进行排序 21 | 22 | long long ans = 0; // 使用 long long 存储结果,以防溢出 23 | int cur = 0; 24 | int left = 0, right = n - 1; 25 | while (left <= right) { 26 | if (cur < a[right]) { 27 | ans += a[right] - cur; 28 | } 29 | cur = a[left]; 30 | right--; 31 | left++; 32 | } 33 | cout << ans << endl; // 输出结果 34 | return 0; 35 | } 36 | ``` 37 | 38 | 39 | 40 | ```Java 41 | import java.util.Arrays; 42 | import java.util.Scanner; 43 | 44 | public class Main { 45 | 46 | public static void main(String[] args) { 47 | Scanner sc = new Scanner(System.in); 48 | int n = sc.nextInt(); 49 | int[] a = new int[n]; 50 | for (int i = 0; i < n; i++) { 51 | a[i] = sc.nextInt(); 52 | } 53 | Arrays.sort(a); 54 | long ans = 0; 55 | int cur = 0; 56 | int left = 0, right = a.length - 1; 57 | while (left <= right) { 58 | if (cur < a[right]) { 59 | ans = ans + a[right] - cur; 60 | } 61 | cur = a[left]; 62 | right--; 63 | left++; 64 | } 65 | System.out.println(ans); 66 | } 67 | } 68 | ``` 69 | -------------------------------------------------------------------------------- /problems/0157.最大化密码复杂度.md: -------------------------------------------------------------------------------- 1 | 2 | # 最大化密码复杂度 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1235) 5 | 6 | 注意**边界处理**,对于字符串的首尾位置,需要特别处理,因为它们只有一个相邻字符。 7 | * 遍历字符串 s,寻找 '?' 字符。 8 | * 对于每个 '?' 字符,选择一个字符填充,使其与前后字符都不同。这样做的目的是最大化密码的复杂度,即尽可能使相邻的字符不同。 9 | * 如果 '?' 是第一个或最后一个字符,或者无法找到与前后都不同的字符,选择与前一个或后一个字符不同的字符。 10 | 11 | 12 | ```CPP 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace std; 18 | 19 | int main() { 20 | int n, m; 21 | string s; 22 | cin >> n >> m >> s; 23 | 24 | if (n == 1) { 25 | cout << 0 << endl; 26 | return 0; 27 | } 28 | 29 | // 统一处理包括左右字符的情况 30 | for (int i = 0; i < n; ++i) { 31 | if (s[i] == '?') { 32 | bool found = false; 33 | for (char j = 'a'; j < 'a' + m; ++j) { 34 | // 避免第一个字符 和 最后一个字符,因为两个字符只有一个相邻字符,没有左右相邻字符 35 | if ((i == 0 || s[i - 1] != j) && (i == n - 1 || s[i + 1] != j)) { 36 | s[i] = j; 37 | found = true; 38 | break; 39 | } 40 | } 41 | // 如果没有找到合适的字符,就和附近字符保持一致 42 | if (!found) { 43 | if (i > 0) s[i] = s[i - 1]; 44 | else s[i] = s[i + 1]; 45 | } 46 | } 47 | } 48 | 49 | // 计算结果 50 | int result = 0; 51 | for (int i = 0; i < n - 1; ++i) { 52 | if (s[i] != s[i + 1]) result++; 53 | } 54 | 55 | cout << result << endl; 56 | return 0; 57 | } 58 | 59 | ``` 60 | -------------------------------------------------------------------------------- /problems/0158.同余方程.md: -------------------------------------------------------------------------------- 1 | 2 | # 同余方程 3 | 4 | 题目链接:https://kamacoder.com/problempage.php?pid=1236 5 | 6 | 我们需要求出满足以下条件的最小正整数 x:`ax≡1 (mod b)` 7 | 8 | 这意味着我们需要找到 x 使得 ax 除以 b 的余数是 1。这个问题实际上是一个典型的 模反元素 问题。 9 | 10 | 解题思路: 11 | 12 | * 为了求出最小的 x,我们可以使用 扩展欧几里得算法 来求出 a 对模 b 的逆元。 13 | * 这个算法能够求解 ax + by = gcd(a, b) 的一组整数解 (x, y),而在 gcd(a, b) = 1 的情况下,x 即为所求的模逆元。 14 | * 扩展欧几里得算法:扩展欧几里得算法可以通过递归或者迭代的方式实现。 15 | 16 | 下面给出C++代码实现: 17 | 18 | ```CPP 19 | #include 20 | using namespace std; 21 | 22 | // 扩展欧几里得:计算 ax + by = gcd(a, b) 的解 23 | long long extended_gcd(long long a, long long b, long long &x, long long &y) { 24 | if (b == 0) { 25 | x = 1; 26 | y = 0; 27 | return a; 28 | } 29 | long long x1, y1; 30 | long long gcd = extended_gcd(b, a % b, x1, y1); 31 | x = y1; 32 | y = x1 - (a / b) * y1; 33 | return gcd; 34 | } 35 | 36 | int main() { 37 | long long a, b; 38 | cin >> a >> b; 39 | 40 | long long x, y; 41 | long long gcd = extended_gcd(a, b, x, y); 42 | 43 | // 由于我们只需要模 b 的正整数解,所以我们要保证 x 是正数 44 | x = (x % b + b) % b; 45 | 46 | cout << x << endl; 47 | 48 | return 0; 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /problems/0159.大整数乘法.md: -------------------------------------------------------------------------------- 1 | 2 | # 大整数乘法 3 | 4 | 题目链接:https://kamacoder.com/problempage.php?pid=1237 5 | 6 | 思路: 7 | 8 | 我们可以使用模拟手算乘法的方法,即「逐位相乘累加」,对于每一位的乘法结果,我们将其加到相应的结果位置上。最终将累加的结果输出。 9 | 10 | 具体步骤: 11 | 12 | * 初始化结果数组:结果数组的长度应该是两个数字长度之和,因为最大长度的结果不会超过这个长度。 13 | * 逐位相乘:从右往左遍历两个字符串的每一位,逐位相乘,并加到结果数组的相应位置。 14 | * 处理进位:在每一步累加之后处理进位,保证每个位置的值小于10。 15 | 16 | 将结果数组转化为字符串:从结果数组的最高位开始,忽略前导零,然后将数组转化为字符串。 17 | 18 | ```CPP 19 | #include 20 | #include 21 | #include 22 | 23 | using namespace std; 24 | 25 | string multiply(string num1, string num2) { 26 | int len1 = num1.size(); 27 | int len2 = num2.size(); 28 | vector result(len1 + len2, 0); 29 | 30 | // 逐位相乘 31 | for (int i = len1 - 1; i >= 0; i--) { 32 | for (int j = len2 - 1; j >= 0; j--) { 33 | int mul = (num1[i] - '0') * (num2[j] - '0'); 34 | int sum = mul + result[i + j + 1]; 35 | 36 | result[i + j + 1] = sum % 10; 37 | result[i + j] += sum / 10; 38 | } 39 | } 40 | 41 | // 将结果转换为字符串,跳过前导零 42 | string product; 43 | for (int num : result) { 44 | if (!(product.empty() && num == 0)) { // 跳过前导零 45 | product.push_back(num + '0'); 46 | } 47 | } 48 | 49 | return product.empty() ? "0" : product; 50 | } 51 | 52 | int main() { 53 | string num1, num2; 54 | cin >> num1 >> num2; 55 | 56 | string result = multiply(num1, num2); 57 | cout << result << endl; 58 | 59 | return 0; 60 | } 61 | 62 | ``` 63 | -------------------------------------------------------------------------------- /problems/0160.二维平面上的折线段.md: -------------------------------------------------------------------------------- 1 | 2 | # 二维平面上的折线段 3 | 4 | 题目链接:https://kamacoder.com/problempage.php?pid=1238 5 | 6 | 这个问题要求我们在一条折线段上,根据移动的固定距离 s 进行标记点的计算。 7 | 8 | 为了实现这一点,我们需要对折线段进行分段处理,并根据每段的长度来确定标记点的位置。 9 | 10 | 解题思路: 11 | 12 | 1. 输入与初步处理: 13 | * 首先,读取所有点的坐标。 14 | * 计算每一段折线的长度,并逐段累积总长度。 15 | 2. 确定标记点: 16 | * 从起点开始,每次沿着折线段前进 s 的距离,直到到达终点。 17 | * 对于每个标记点,根据当前段的起点和终点,计算出该点的精确坐标。 18 | 3. 输出所有标记点的坐标,格式为 x, y。 19 | 20 | ```CPP 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | using namespace std; 28 | 29 | // 定义一个点的结构体 30 | struct Point { 31 | double x, y; 32 | }; 33 | 34 | // 计算两点之间的距离 35 | double distance(const Point& a, const Point& b) { 36 | return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); 37 | } 38 | 39 | int main() { 40 | int n; 41 | cin >> n; 42 | 43 | vector points(n); 44 | for (int i = 0; i < n; i++) { 45 | cin >> points[i].x >> points[i].y; 46 | } 47 | 48 | double s; 49 | cin >> s; 50 | 51 | double total_length = 0.0; 52 | vector segment_lengths(n - 1); 53 | 54 | // 计算每段长度和总长度 55 | for (int i = 0; i < n - 1; i++) { 56 | segment_lengths[i] = distance(points[i], points[i + 1]); 57 | total_length += segment_lengths[i]; 58 | } 59 | 60 | // 从起点开始标记 61 | Point current_point = points[0]; 62 | double accumulated_distance = 0.0; 63 | 64 | cout << fixed << setprecision(5); 65 | cout << current_point.x << ", " << current_point.y << endl; 66 | 67 | while (accumulated_distance + s <= total_length) { 68 | accumulated_distance += s; 69 | double remaining_distance = accumulated_distance; 70 | 71 | for (int i = 0; i < n - 1; i++) { 72 | if (remaining_distance <= segment_lengths[i]) { 73 | double ratio = remaining_distance / segment_lengths[i]; 74 | double new_x = points[i].x + ratio * (points[i + 1].x - points[i].x); 75 | double new_y = points[i].y + ratio * (points[i + 1].y - points[i].y); 76 | current_point = {new_x, new_y}; 77 | cout << current_point.x << ", " << current_point.y << endl; 78 | break; 79 | } else { 80 | remaining_distance -= segment_lengths[i]; 81 | } 82 | } 83 | } 84 | 85 | return 0; 86 | } 87 | 88 | ``` 89 | -------------------------------------------------------------------------------- /problems/0161.讨厌鬼的组合帖子.md: -------------------------------------------------------------------------------- 1 | 2 | # 讨厌鬼的组合帖子 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1239) 5 | 6 | 这个问题本质上是要找到两个数组的子集,使得这两个子集之间的差的绝对值最大。 7 | 8 | 问题可以简化为寻找两个数列之间最大可能的差的绝对值。 9 | 10 | 贪心思路如下: 11 | 12 | 计算差异,首先,我们可以计算每个帖子的点赞数和点踩数的差值 d[i] = a[i] - b[i]。这样问题就转化为选择这些差值的一个子集,使得子集中所有元素的和的绝对值最大。 13 | 14 | 遍历可能性,要使得一个数的绝对值尽可能大,可以尝试最大化这个数,或者最小化这个数(使其尽可能小于零)。我们可以分别尝试将所有正的差值加在一起,以及将所有负的差值加在一起。 15 | 16 | 计算最大吸引度: 17 | 18 | * 将所有正的差值求和得到一个总和。 19 | * 将所有负的差值求和得到另一个总和。 20 | * 最后,吸引度即为这两个总和的绝对值中的较大者。 21 | 22 | 23 | ```CPP 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | using namespace std; 30 | 31 | int main() { 32 | int n; 33 | cin >> n; 34 | 35 | vector a(n), b(n); 36 | for (int i = 0; i < n; ++i) { 37 | cin >> a[i]; 38 | } 39 | for (int i = 0; i < n; ++i) { 40 | cin >> b[i]; 41 | } 42 | 43 | long long positive_sum = 0; 44 | long long negative_sum = 0; 45 | 46 | for (int i = 0; i < n; ++i) { 47 | int difference = a[i] - b[i]; 48 | if (difference > 0) { 49 | positive_sum += difference; 50 | } else if (difference < 0) { 51 | negative_sum += difference; 52 | } 53 | } 54 | 55 | // 最大吸引度是正总和或负总和的绝对值中的较大者 56 | cout << max(abs(positive_sum), abs(negative_sum)) << endl; 57 | 58 | return 0; 59 | } 60 | ``` 61 | 62 | -------------------------------------------------------------------------------- /problems/0163.优秀数组.md: -------------------------------------------------------------------------------- 1 | 2 | # 优秀数组 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1241) 5 | 6 | ## 解题思路 7 | 8 | 1、初始分析 9 | 10 | - 给定一个排列 `p`,我们首先构建一个 `pos` 数组,使得 `pos[i]` 表示 `i` 在排列 `p` 中的位置。 11 | - 我们需要判断数组 `a` 是否是一个优秀数组,即 `pos[a[i]] < pos[a[i+1]] <= pos[a[i]] + d` 对于所有 `i` 都成立。 12 | - 我们的目标是通过最少的相邻元素交换,使得数组 `a` 不再是一个优秀数组。 13 | 14 | 2、思路 15 | 16 | - 要使数组 `a` 不再是优秀数组,我们只需要打破条件 `pos[a[i]] < pos[a[i+1]] <= pos[a[i]] + d` 中的某一个。 17 | - 一种简单的做法是让 `pos[a[i]]` 和 `pos[a[i+1]]` 之间的距离超过 `d`,或者直接让 `pos[a[i]] >= pos[a[i+1]]`。 18 | 19 | 3、具体方法 20 | 21 | - 只需要考虑 `a` 中相邻元素的顺序,并判断如何交换 `p` 中相邻元素使得其顺序被打破。 22 | - 假设我们需要在 `p` 中交换某些元素来实现上述目标,那么最小的交换次数是将 `a[i]` 和 `a[i+1]` 的位置交换。 23 | - 如果 `pos[a[i]] + 1 == pos[a[i+1]]`,则需要一步交换。 24 | 25 | 4、特别情况 26 | 27 | - 还需要考虑,如果通过交换相邻元素无法解决问题的情况。比如 `pos[a[i+1]]` 的位置无法移到 `pos[a[i]]` 的前面或超过 `d`。 28 | 29 | C++代码如下: 30 | 31 | 32 | ```cpp 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | using namespace std; 39 | 40 | int main() { 41 | int n, m, d; 42 | cin >> n >> m >> d; 43 | 44 | vector p(n + 1); 45 | vector pos(n + 1); 46 | 47 | // 读取排列 p,并构建位置数组 pos 48 | for (int i = 1; i <= n; i++) { 49 | cin >> p[i]; 50 | pos[p[i]] = i; 51 | } 52 | 53 | vector a(m); 54 | for (int i = 0; i < m; i++) { 55 | cin >> a[i]; 56 | } 57 | 58 | int min_operations = INT_MAX; 59 | 60 | // 遍历数组 a 的相邻元素 61 | for (int i = 0; i < m - 1; i++) { 62 | int current_pos = pos[a[i]]; 63 | int next_pos = pos[a[i + 1]]; 64 | 65 | // 检查 pos[a[i]] < pos[a[i+1]] <= pos[a[i]] + d 是否成立 66 | if (current_pos < next_pos && next_pos <= current_pos + d) { 67 | // 计算需要的最少操作次数 68 | int distance = next_pos - current_pos; 69 | 70 | // Case 1: 交换 current_pos 和 next_pos 71 | min_operations = min(min_operations, distance); 72 | 73 | // Case 2: 如果 next_pos + d <= n,考虑使 pos[a[i+1]] 超过 pos[a[i]] + d 74 | if (current_pos + d + 1 <= n) { 75 | min_operations = min(min_operations, d + 1 - distance); 76 | } 77 | } else { 78 | min_operations = 0; 79 | } 80 | } 81 | 82 | cout << min_operations << endl; 83 | return 0; 84 | } 85 | 86 | ``` 87 | 88 | 时间复杂度为 O(m) 89 | -------------------------------------------------------------------------------- /problems/0164.升序数组.md: -------------------------------------------------------------------------------- 1 | # 升序数组 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1241) 4 | 5 | ## 解题思路 6 | 7 | 贪心思路 8 | 9 | - **计算相邻元素差值**: 10 | - 对于数组 `a`,计算每对相邻元素的差值 `diff[i] = a[i+1] - a[i]`。 11 | - 如果 `diff[i]` 为负数,意味着 `a[i+1]` 比 `a[i]` 小或相等,需要通过操作使 `a[i+1]` 变大。 12 | 13 | - **确定最小操作次数**: 14 | - 计算所有相邻元素中的最小差值 `minDifference`,即 `minDifference = min(diff[i])`。 15 | - 如果 `minDifference` 为负数或零,则需要进行 `-minDifference + 1` 次操作,使得 `a[i+1]` 大于 `a[i]`,从而使数组严格递增。 16 | 17 | - **实现细节**: 18 | - 遍历数组的每对相邻元素,找出最小的差值。 19 | - 根据最小差值,计算出最少的操作次数。 20 | 21 | 22 | 23 | ```CPP 24 | #include 25 | #include 26 | #include 27 | 28 | using namespace std; 29 | 30 | int main() 31 | { 32 | int n; 33 | cin >> n; 34 | 35 | vector arr(n); // 用于存储输入数组 36 | vector differences; // 用于存储相邻元素的差值 37 | 38 | for(int i = 0; i < n; i++) { 39 | cin >> arr[i]; 40 | if(i > 0) differences.push_back(arr[i] - arr[i - 1]); 41 | 42 | } 43 | 44 | int minDifference = INT_MAX; 45 | 46 | // 寻找最小的差值 47 | for(int diff : differences) { 48 | if(diff < minDifference) { 49 | minDifference = diff; 50 | } 51 | } 52 | 53 | // 如果最小差值是负数或零,计算所需的操作次数 54 | int minOperations = max(0, -minDifference + 1); 55 | 56 | cout << minOperations << endl; 57 | 58 | return 0; 59 | } 60 | 61 | ``` 62 | 关于 `-minDifference + 1` 为什么要 + 1 解释: 63 | 64 | 对于数组 `a` 中相邻的两个元素 `a[i]` 和 `a[i+1]`,我们计算它们的差值 `diff = a[i+1] - a[i]`。 65 | 66 | - **目标**:要使 `a[i] < a[i+1]`,需要 `diff > 0`。 67 | - 如果 `diff < 0`,说明 `a[i+1]` 比 `a[i]` 小,这时候 `a` 不是严格递增的。 68 | - 如果 `diff = 0`,说明 `a[i+1]` 和 `a[i]` 相等,这时也不满足严格递增。 69 | 70 | 解释 `-minDifference + 1` 71 | 72 | 1. **当 `minDifference < 0` 时**: 73 | - 假设 `minDifference` 是所有相邻差值中的最小值,并且它是一个负数。 74 | - 例如,`minDifference = -3`,表示 `a[i+1] - a[i] = -3`,也就是 `a[i+1]` 比 `a[i]` 小 `3`。 75 | - 要让 `a[i+1] > a[i]`,我们至少需要使 `a[i+1] - a[i]` 从 `-3` 增加到 `1`。因此需要增加 `4`,即 `(-(-3)) + 1 = 3 + 1 = 4` 次操作。 76 | 77 | 2. **当 `minDifference = 0` 时**: 78 | - `minDifference` 等于 `0`,表示 `a[i+1] - a[i] = 0`,即 `a[i+1]` 和 `a[i]` 相等。 79 | - 为了使 `a[i+1] > a[i]`,我们至少需要进行一次操作,使得 `a[i+1]` 大于 `a[i]`。 80 | 81 | 82 | -------------------------------------------------------------------------------- /problems/0165.最大字典序无重复串.md: -------------------------------------------------------------------------------- 1 | 2 | # 最大字典序无重复串 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1243) 5 | 6 | 7 | ## 解题思路 8 | 9 | 贪心思路 10 | 11 | 为了保证字典序最大,我们优先放置字母 `b`,然后再放置字母 `a`。在放置字符时,我们还需注意不能超过连续 `k` 次相同字符: 12 | 13 | - 如果当前已经连续放置了 `k` 次相同字符,必须切换到另一个字符。 14 | - 每次放置字符后,相应的字符数量减少,同时更新当前字符的连续计数。 15 | 16 | 实现步骤: 17 | 18 | - **初始化**:根据输入的 `x`, `y`, `k` 值,检查是否有可能构造出满足条件的字符串。初始化结果字符串的大小,并设置初始计数器。 19 | - **循环放置字符**: 20 | - 优先放置字符 `b`,如果 `b` 的数量已经足够,或者已经放置了 `k` 次字符 `b`,则放置字符 `a`。 21 | - 如果已经放置了 `k` 次相同字符,则强制切换到另一个字符。 22 | 23 | C++代码如下: 24 | 25 | ```CPP 26 | #include 27 | #include 28 | using namespace std; 29 | 30 | int main() { 31 | int countA, countB, maxRepeat; 32 | cin >> countA >> countB >> maxRepeat; 33 | 34 | // 检查是否有可能生成满足条件的字符串 35 | if (countA > (countB + 1) * maxRepeat || countB > (countA + 1) * maxRepeat) { 36 | cout << -1 << endl; 37 | return 0; 38 | } 39 | 40 | string result(countA + countB, ' '); // 预先分配字符串大小 41 | int currentA = 0, currentB = 0; // 当前连续 'a' 和 'b' 的计数 42 | int pos = 0; // 当前填充位置 43 | 44 | while (countA > 0 || countB > 0) { 45 | // 当可以继续添加 'a' 或 'b' 且没有超过最大连续限制时 46 | if (currentA < maxRepeat && currentB < maxRepeat) { 47 | if (countA <= countB * maxRepeat) { 48 | result[pos++] = 'b'; 49 | countB--; 50 | currentB++; 51 | currentA = 0; 52 | } else { 53 | result[pos++] = 'a'; 54 | countA--; 55 | currentA++; 56 | currentB = 0; 57 | } 58 | } 59 | 60 | // 当当前字符达到最大连续限制时,切换到另一个字符 61 | if (currentA == maxRepeat || currentB == maxRepeat) { 62 | if (result[pos - 1] == 'a') { 63 | result[pos++] = 'b'; 64 | countB--; 65 | currentB = 1; 66 | currentA = 0; 67 | } else { 68 | result[pos++] = 'a'; 69 | countA--; 70 | currentA = 1; 71 | currentB = 0; 72 | } 73 | } 74 | } 75 | 76 | cout << result << endl; 77 | return 0; 78 | } 79 | 80 | ``` 81 | 82 | 时间复杂度:O(n) 83 | -------------------------------------------------------------------------------- /problems/0167.黑白块.md: -------------------------------------------------------------------------------- 1 | 2 | # 黑白块 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1245) 5 | 6 | ## 思路分析 7 | 8 | 本题本质是一个**带权图的最短路径问题**,其中白色网格(0)表示权重为0,黑色网格(1)表示权重为1。目标是从起点 (1,1) 走到终点 (n,m),经过的黑色网格数最少。 9 | 10 | 由于这是一个有特殊权重(0和1)的最短路径问题,我们可以使用贪心 + BFS 。 11 | 12 | 贪心:就是在队列里优先走选白色格子。 13 | 14 | BFS 算法: 15 | 16 | 1. 使用一个双端队列,存储待处理的节点。我们优先处理代价小的路径。 17 | 2. 如果走到一个白色格子(0),我们将其放到队列的前端,因为不增加代价。(优先取出来) 18 | 3. 如果走到一个黑色格子(1),我们将其放到队列的后端,因为会增加代价。(尽量靠后) 19 | 4. 最终到达终点时,输出代价(经过的黑色格子数)。 20 | 21 | 22 | ## 代码实现 23 | 24 | 25 | ```CPP 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | using namespace std; 32 | 33 | // 四个方向移动 34 | const int dx[4] = {0, 0, 1, -1}; 35 | const int dy[4] = {1, -1, 0, 0}; 36 | 37 | int main() { 38 | int n, m; 39 | cin >> n >> m; 40 | 41 | vector> grid(n, vector(m)); 42 | for (int i = 0; i < n; i++) { 43 | for (int j = 0; j < m; j++) { 44 | cin >> grid[i][j]; 45 | } 46 | } 47 | 48 | // 最小经过的黑色格子数,初始化为一个很大的值 49 | vector> dist(n, vector(m, INT_MAX)); 50 | 51 | // 使用双端队列 52 | deque> dq; 53 | 54 | // 起点 (0,0) 55 | dist[0][0] = grid[0][0]; // 起点是否是黑色 56 | dq.push_back({0, 0}); 57 | 58 | // BFS 开始 59 | while (!dq.empty()) { 60 | auto [x, y] = dq.front(); 61 | dq.pop_front(); 62 | 63 | // 遍历四个方向 64 | for (int i = 0; i < 4; i++) { 65 | int nx = x + dx[i]; 66 | int ny = y + dy[i]; 67 | 68 | // 检查是否越界 69 | if (nx >= 0 && ny >= 0 && nx < n && ny < m) { 70 | int new_dist = dist[x][y] + grid[nx][ny]; 71 | 72 | // 如果找到更短路径 73 | if (new_dist < dist[nx][ny]) { 74 | dist[nx][ny] = new_dist; 75 | 76 | // 白色格子优先放前端,黑色格子放后端 77 | if (grid[nx][ny] == 0) { 78 | dq.push_front({nx, ny}); 79 | } else { 80 | dq.push_back({nx, ny}); 81 | } 82 | } 83 | } 84 | } 85 | } 86 | 87 | // 输出结果 88 | cout << dist[n-1][m-1] << endl; 89 | 90 | return 0; 91 | } 92 | ``` 93 | 94 | 每个格子最多进出队列两次,时间复杂度为 O(n×m) 95 | -------------------------------------------------------------------------------- /problems/0168.删除三元组.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # 168. 删除三元组 5 | 6 | [题目链接](https://kamacoder.com/problempage.php?pid=1246) 7 | 8 | 解题思路: 9 | 10 | 统计元素频率:通过遍历数组并记录每个元素(1 到 6)出现的次数。 11 | 12 | 贪心算法:在满足条件的情况下,优先匹配能够组成三元组的元素。每匹配到一个三元组,就减少对应元素的计数,重复这个过程直到没有更多可以组成的三元组。 13 | 14 | 特殊情况处理:如果数组长度较小,或者没有办法找到满足条件的三元组,算法会返回 0 或正确的结果。 15 | 16 | 17 | ```CPP 18 | #include 19 | #include 20 | #include 21 | 22 | using namespace std; 23 | 24 | int main() { 25 | int n; 26 | cin >> n; 27 | vector a(n); 28 | 29 | // 读取数组 30 | for (int i = 0; i < n; i++) { 31 | cin >> a[i]; 32 | } 33 | 34 | // 统计每个元素的出现次数,元素值范围是1到6 35 | vector count(7, 0); // 使用索引1-6 36 | for (int i = 0; i < n; i++) { 37 | count[a[i]]++; 38 | } 39 | 40 | int result = 0; 41 | 42 | // 贪心地找三元组 (x, y, z),满足x < y < z, y是x的倍数, z是y的倍数 43 | while (count[1] > 0 && count[2] > 0 && count[4] > 0) { 44 | count[1]--; 45 | count[2]--; 46 | count[4]--; 47 | result++; 48 | } 49 | 50 | while (count[1] > 0 && count[3] > 0 && count[6] > 0) { 51 | count[1]--; 52 | count[3]--; 53 | count[6]--; 54 | result++; 55 | } 56 | 57 | // 输出最多可以执行的操作次数 58 | cout << result << endl; 59 | 60 | return 0; 61 | } 62 | 63 | ``` 64 | -------------------------------------------------------------------------------- /problems/0169.非连续合法字符串.md: -------------------------------------------------------------------------------- 1 | 2 | # 169. 非连续合法字符串 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1247) 5 | 6 | 7 | 连续相同字母检查:在遍历字符串时,判断当前字符与后两个字符是否相同,如果是则删除一个字符(即从字符串中删除第三个连续的字符)。 8 | 9 | "aabb" 模式检查:检查当前字符和后一个字符是否与其后两个字符构成 "aabb" 模式(即两个相同字符跟着另两个相同字符且这两组字符不同)。如果是则删除其中的一个字符。 10 | 11 | 累计删除次数:在上述操作中,每删除一个字符,就增加删除次数。 12 | 13 | 14 | ```CPP 15 | #include 16 | #include 17 | 18 | using namespace std; 19 | 20 | 21 | 22 | int main() { 23 | string s; 24 | cin >> s; // 输入字符串 25 | int n = s.length(); // 获取字符串的长度 26 | int deletions = 0; // 记录删除的字符个数 27 | 28 | // 遍历字符串 29 | for (int i = 0; i < n; ++i) { 30 | // 步骤 1:检查是否有连续三个相同的字符 31 | if (i + 2 < n && s[i] == s[i + 1] && s[i] == s[i + 2]) { 32 | deletions++; // 需要删除一个字符 33 | // 模拟删除第三个连续字符 34 | s.erase(i + 2, 1); 35 | n--; // 删除后更新字符串长度 36 | i--; // 重新检查当前字符位置 37 | } 38 | 39 | // 步骤 2:检查是否有 "aabb" 模式 40 | if (i + 3 < n && s[i] == s[i + 1] && s[i + 2] == s[i + 3] && s[i] != s[i + 2]) { 41 | deletions++; // 需要删除一个字符 42 | // 模拟删除第四个字符 43 | s.erase(i + 3, 1); 44 | n--; // 删除后更新字符串长度 45 | i--; // 重新检查当前字符位置 46 | } 47 | } 48 | cout << deletions << endl; // 输出最少需要删除的字符个数 49 | return 0; 50 | } 51 | 52 | 53 | ``` 54 | 55 | 时间复杂度:O(n),只需遍历字符串一次,删除操作最多也是 O(n),因此总复杂度为 O(n),能够处理长度为 10^5 的输入。 56 | 57 | 空间复杂度:O(1),只需常数级别的额外空间存储删除计数器和辅助变量。 58 | -------------------------------------------------------------------------------- /problems/0170.权值不等的路径方案.md: -------------------------------------------------------------------------------- 1 | 2 | # 170.权值不等的路径方案 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1248) 5 | 6 | 解题思路 7 | 8 | 树的构建:首先根据输入的父子关系构建一棵以 1 号节点为根节点的树。用邻接表表示树的结构,这样我们可以快速查找每个节点的子节点。 9 | 10 | DFS(深度优先搜索)遍历:使用 DFS 来遍历树中的每个节点,从每个节点开始,递归地构建所有可能的路径。 11 | 12 | 在每条路径上,我们用一个集合 unordered_set 来存储当前路径上的权值,保证路径中的权值都是不同的。 13 | 14 | 合法路径统计:每次访问到一个合法的节点时,记录路径的数量 result++。 15 | 16 | 回溯操作:DFS 完成后,恢复路径状态,即将当前节点的权值从集合中删除,以便后续路径的计算不受影响。 17 | 18 | 代码如下: 19 | 20 | ```CPP 21 | #include 22 | #include 23 | #include 24 | 25 | using namespace std; 26 | 27 | int result = 0; // 记录合法路径的总数 28 | vector> son_all; // 存储树的子节点结构 29 | vector weight_all; // 存储每个节点的权值 30 | 31 | // 深度优先搜索函数 32 | void dfs(int node, unordered_set& usedValues) { 33 | // 如果当前节点的权值已经在路径中,停止递归 34 | if (usedValues.count(weight_all[node]) != 0) { 35 | return; 36 | } 37 | 38 | // 当前节点的权值合法,加入到路径中 39 | usedValues.insert(weight_all[node]); 40 | result++; // 每当访问到一个合法节点,路径计数+1 41 | 42 | // 遍历该节点的所有子节点 43 | for (int child : son_all[node]) { 44 | dfs(child, usedValues); 45 | } 46 | 47 | // 回溯:移除当前节点的权值,恢复状态 48 | usedValues.erase(weight_all[node]); 49 | } 50 | 51 | int main() { 52 | int n; 53 | cin >> n; // 读取节点数量 54 | 55 | son_all.resize(n + 1); // 初始化存储子节点的数组 56 | weight_all.resize(n + 1); // 初始化每个节点的权值数组 57 | 58 | // 读取每个节点的父节点编号,建立树的结构 59 | for (int i = 2; i <= n; i++) { 60 | int parent; 61 | cin >> parent; 62 | son_all[parent].push_back(i); // 记录父子关系 63 | } 64 | 65 | // 读取每个节点的权值 66 | for (int i = 1; i <= n; i++) { 67 | cin >> weight_all[i]; 68 | } 69 | 70 | unordered_set usedValues; // 用于记录当前路径中的权值 71 | 72 | // 对每个节点都进行DFS,计算所有可能的路径 73 | for (int i = 1; i <= n; i++) { 74 | dfs(i, usedValues); 75 | } 76 | 77 | // 输出结果:合法路径的总数 78 | cout << result << endl; 79 | 80 | return 0; 81 | } 82 | 83 | ``` 84 | -------------------------------------------------------------------------------- /problems/0171.卡牌对弈.md: -------------------------------------------------------------------------------- 1 | 2 | # 171. 卡牌对弈 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1249) 5 | 6 | ### 题解 7 | 8 | 问题的本质是找到一种策略,使得多多的卡牌在每一轮对弈中,尽可能多的打败对手的卡牌。双方的卡牌数量相同,且卡牌只能用一次,因此需要找到多多出牌的最佳顺序来赢得尽可能多的对局。 9 | 10 | #### 解决思路 11 | 12 | 1. **排序**: 13 | 为了保证多多赢得更多的局数,可以采用贪心策略。具体思路是: 14 | - 将多多的卡牌和对方的卡牌分别排序。 15 | - 多多从最小的卡牌开始,尽量用当前最小的卡牌去战胜对方当前最小的卡牌。 16 | 17 | 2. **贪心策略**: 18 | - 用多多的最小卡牌和对方的最小卡牌比较,如果多多的卡牌大于对方的卡牌,则多多赢得这一局,双方都拿掉当前卡牌,进入下一轮比较。 19 | - 如果多多的卡牌不能赢得这一局,则多多保留当前卡牌,用对方的下一个卡牌继续比较,直到找到合适的卡牌进行比较,或者没有合适的卡牌为止。 20 | 21 | #### 算法步骤 22 | 23 | - 将多多的卡牌和对方的卡牌分别排序。 24 | - 通过双指针法,一一比较多多和对方的卡牌。 25 | - 每当多多的卡牌战胜对方的卡牌时,胜场数加一,并且两张卡牌都移除。 26 | 27 | #### 时间复杂度 28 | 29 | - 排序的时间复杂度是 \(O(n \log n)\),遍历卡牌的时间复杂度是 \(O(n)\),因此总的时间复杂度为 \(O(n \log n)\),可以应对题目中的数据规模。 30 | 31 | ### 代码实现 32 | 33 | ```CPP 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | using namespace std; 40 | 41 | int main() { 42 | int n; 43 | cin >> n; 44 | 45 | vector duoduo_cards(n); 46 | vector opponent_cards(n); 47 | 48 | // 输入多多的卡牌 49 | for (int i = 0; i < n; ++i) { 50 | cin >> duoduo_cards[i]; 51 | } 52 | 53 | // 输入对手的卡牌 54 | for (int i = 0; i < n; ++i) { 55 | cin >> opponent_cards[i]; 56 | } 57 | 58 | // 排序两边的卡牌 59 | sort(duoduo_cards.begin(), duoduo_cards.end()); 60 | sort(opponent_cards.begin(), opponent_cards.end()); 61 | 62 | int i = 0, j = 0; // 双指针 63 | int wins = 0; // 多多的胜场数 64 | 65 | // 用双指针比较卡牌 66 | while (i < n && j < n) { 67 | if (duoduo_cards[i] > opponent_cards[j]) { 68 | // 如果多多的卡牌比对手的卡牌大,多多赢一局 69 | wins++; 70 | j++; // 对手的卡牌移除 71 | } 72 | // 无论胜负,多多都要移除当前卡牌 73 | i++; 74 | } 75 | 76 | cout << wins << endl; 77 | 78 | return 0; 79 | } 80 | 81 | ``` 82 | -------------------------------------------------------------------------------- /problems/0172.股票上涨序列.md: -------------------------------------------------------------------------------- 1 | 2 | # 172. 股票上涨序列 3 | 4 | [题目链接](https://kamacoder.com/problemcontest.php?cid=1061&pid=1) 5 | 6 | 7 | ### 题解 8 | 9 | 本题的核心在于控制股票的上涨系数,使其不超过给定的百分比 `k`,同时对股票价格的变更尽可能小。每组测试样例都有一系列股票的初始价格和随后的上涨数值,要求我们适当调整这些上涨数值以满足上涨系数不超过 `k%` 的条件。 10 | 11 | #### 贪心策略 12 | 13 | 1. 我们逐月检查上涨系数是否合规。如果不合规,则调整之前的股票价格总和 p0 + p1 + ... + pi-1 增大到满足条件的最小值。 14 | 15 | 2. 为了使变更的总和尽可能小,我们可以将股票的初始价格进行最小的调整,确保每个月的上涨系数都不会超过 `k%`。 16 | 17 | #### 算法步骤 18 | 19 | 1. 对于每个测试样例,初始化总和为第一个价格 p0 。 20 | 21 | 2. 从第二个月开始,逐步检查上涨系数是否满足条件: 22 | 23 | pi <= k / 100 * sum 24 | 25 | 如果不满足条件,则需要将总和 \( \text{sum} \) 增大,并记录调整量。 26 | 27 | 3. 最后输出每个测试样例的最小变更总和。 28 | 29 | ### 代码实现 30 | 31 | ```cpp 32 | #include 33 | #include 34 | #include 35 | 36 | using namespace std; 37 | 38 | // 计算最小变更的总和 39 | long long min_changes(int n, int k, vector& prices) { 40 | long long total_sum = prices[0]; // 初始股票价格总和 41 | long long total_change = 0; // 总变更量 42 | 43 | for (int i = 1; i < n; ++i) { 44 | // 计算上涨系数的最大允许值 45 | long long max_increase = total_sum * k / 100; 46 | 47 | // 如果当前上涨值超过了最大允许值 48 | if (prices[i] > max_increase) { 49 | // 需要增加之前的总和 50 | long long required_sum = ceil(100.0 * prices[i] / k); 51 | total_change += (required_sum - total_sum); 52 | total_sum = required_sum; 53 | } 54 | 55 | // 更新当前总和 56 | total_sum += prices[i]; 57 | } 58 | 59 | return total_change; 60 | } 61 | 62 | int main() { 63 | int t; 64 | cin >> t; // 测试用例数量 65 | 66 | while (t--) { 67 | int n, k; 68 | cin >> n >> k; 69 | 70 | vector prices(n); 71 | for (int i = 0; i < n; ++i) { 72 | cin >> prices[i]; 73 | } 74 | 75 | cout << min_changes(n, k, prices) << endl; 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | -------------------------------------------------------------------------------- /problems/0173.编辑距离内的子串.md: -------------------------------------------------------------------------------- 1 | 2 | # 173. 编辑距离内的子串 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1251) 5 | 6 | ## 思路 7 | 8 | 寻找现有的 "PDD" 子串:首先在给定的字符串中寻找已有的 "PDD" 子串,并将其标记为已处理的部分(避免重复使用)。 9 | 10 | 部分匹配:接着,处理剩下的子串。如果已经有部分匹配(如 "PD" 或 "DD"),可以通过一次编辑操作将其变为完整的 "PDD" 子串。 11 | 12 | 单个字符匹配:对于单个的 "P" 或 "D" 字符,如果有两次操作的额度,可以通过添加两个字符来形成 "PDD"。 13 | 14 | 剩余操作的处理:最后,如果还有剩余的编辑次数,考虑通过插入操作,每三次插入操作可以增加一个 "PDD" 子串。 15 | 16 | 代码如下: 17 | 18 | ```CPP 19 | 20 | #include 21 | #include 22 | using namespace std; 23 | 24 | int getres(string &s, int &m) { 25 | int n = s.size(); 26 | int res = 0; 27 | 28 | // Step 1: 统计现有的完整 "PDD" 子串 29 | for (int i = 0; i < n - 2; i++) { 30 | if (s[i] == 'P' && s[i + 1] == 'D' && s[i + 2] == 'D') { 31 | res++; 32 | s[i] = 'X'; // 标记为已处理 33 | s[i + 1] = 'X'; 34 | s[i + 2] = 'X'; 35 | } 36 | } 37 | 38 | // Step 2: 处理部分匹配的 "PD" 或 "DD" 39 | for (int i = 0; i < n - 1; i++) { 40 | if ((s[i] == 'P' && s[i + 1] == 'D') || (s[i] == 'D' && s[i + 1] == 'D')) { 41 | if (m > 0) { // 如果还有编辑次数剩余 42 | res++; 43 | m--; // 使用一次编辑操作 44 | s[i] = 'X'; // 标记为已处理 45 | s[i + 1] = 'X'; 46 | } else { 47 | return res; // 没有剩余编辑次数,返回当前结果 48 | } 49 | } 50 | } 51 | 52 | // Step 3: 处理单个字符 "P" 或 "D" 53 | for (int i = 0; i < n; i++) { 54 | if (s[i] == 'P' || s[i] == 'D') { 55 | if (m > 1) { // 如果至少有2次编辑操作 56 | res++; 57 | m -= 2; // 使用两次编辑操作 58 | s[i] = 'X'; // 标记为已处理 59 | } else { 60 | return res; // 没有足够的编辑次数,返回当前结果 61 | } 62 | } 63 | } 64 | 65 | // Step 4: 通过剩余编辑次数直接插入 "PDD" 66 | res += m / 3; // 每3次操作可以插入一个 "PDD" 67 | 68 | return res; 69 | } 70 | 71 | int main() { 72 | int T; 73 | cin >> T; 74 | while (T--) { 75 | int m; 76 | cin >> m; 77 | string s; 78 | cin >> s; 79 | int res = getres(s, m); 80 | cout << res << endl; 81 | } 82 | } 83 | 84 | ``` 85 | -------------------------------------------------------------------------------- /problems/0174.魔物入侵.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # 174. 魔物入侵 5 | 6 | [题目链接](https://kamacoder.com/problempage.php?pid=1252) 7 | 8 | 二分查找: 9 | 10 | 我们可以使用二分查找来确定答案,二分的目标是空缺的长度,也就是我们要在允许的时间内找到最大的空缺。 11 | 12 | 每次我们用一个长度 mid 来猜测最大的空缺长度,然后通过动态规划 dp 来计算,看看是否在这个最大空缺长度下能完成任务。 13 | 14 | 动态规划: 15 | 16 | 我们定义 dp[i] 为在第 i 个位置结束时,搭建防御塔所花费的最少时间。 17 | 18 | 使用双端队列 deque 来优化 DP 状态的转移,因为我们只需要关注之前的 mid 个位置来决定当前的状态。 19 | 20 | 我们需要检查从 n-len 到 n 的位置,看这些位置是否能在时间限制 t 内完成建造。 21 | 22 | 滑动窗口: 23 | 24 | 利用双端队列的性质,每次我们维护一个长度为 mid 的滑动窗口,确保队列中的位置能够有效参与状态转移。 25 | 26 | 如果 dp[n - len] 的值小于等于 t,说明可以在时间 t 内建造完成,允许的空缺长度可以更小。 27 | 28 | ```CPP 29 | 30 | #include 31 | using namespace std; 32 | 33 | // 检查是否可以在时间限制内完成建造 34 | bool canBuild(const vector& buildTimes, long long n, long long timeLimit, long long maxGap) { 35 | vector dp(n + 1, LLONG_MAX); // 初始化 dp 数组为最大值 36 | dp[0] = 0; // 初始位置耗时为 0 37 | deque dq; 38 | dq.push_back(0); 39 | 40 | // 动态规划,更新每个位置的 dp 值 41 | for (int i = 1; i <= n; ++i) { 42 | if (!dq.empty() && i - dq.front() - 1 > maxGap) { 43 | dq.pop_front(); // 超出范围,移除队首元素 44 | } 45 | dp[i] = dp[dq.front()] + buildTimes[i]; // 更新 dp[i] 46 | while (!dq.empty() && dp[dq.back()] >= dp[i]) { 47 | dq.pop_back(); // 保持队列递增 48 | } 49 | dq.push_back(i); // 将当前 i 加入队列 50 | } 51 | 52 | // 检查从第 n-maxGap 到 n 的位置,是否可以在 timeLimit 时间内完成 53 | for (long long i = n; i >= n - maxGap; --i) { 54 | if (dp[i] <= timeLimit) { 55 | return true; // 能在时间限制内完成 56 | } 57 | } 58 | return false; // 超出时间限制 59 | } 60 | 61 | int main() { 62 | int testCases; 63 | cin >> testCases; 64 | while (testCases--) { 65 | long long n, timeLimit; 66 | cin >> n >> timeLimit; 67 | vector buildTimes(n + 1); 68 | for (int i = 1; i <= n; ++i) { 69 | cin >> buildTimes[i]; // 输入每个位置的建造耗时 70 | } 71 | 72 | long long left = 0, right = n; 73 | 74 | // 二分查找最大空缺的长度 75 | while (left < right) { 76 | long long mid = (left + right) / 2; 77 | 78 | // 调用独立的 canBuild 函数 79 | if (canBuild(buildTimes, n, timeLimit, mid)) { 80 | right = mid; // 如果满足条件,缩小右边界 81 | } else { 82 | left = mid + 1; // 否则增加左边界 83 | } 84 | } 85 | cout << left << endl; // 输出最小的最大空缺值 86 | } 87 | return 0; 88 | } 89 | 90 | ``` 91 | -------------------------------------------------------------------------------- /problems/0175.阴阳师.md: -------------------------------------------------------------------------------- 1 | 2 | # 175. 阴阳师 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1253) 5 | 6 | 理解回本条件: 7 | 8 | * 购买永久勾玉卡的费用为 a 勾玉。 9 | * 每天登录返还 b 勾玉。 10 | * 要回本,即要满足:b * d >= a,其中 d 是登录的天数。 11 | 12 | 求解登录天数: 13 | 14 | 从上述不等式可以推导出: d >= a/b 15 | 16 | 因为 d 必须是一个整数,所以可以使用向上取整的方式计算 d: d = (a + b + 1)/b 17 | 18 | 19 | 20 | ```CPP 21 | #include 22 | 23 | int main() { 24 | int T; 25 | std::cin >> T; 26 | 27 | while (T--) { 28 | long long a, b; 29 | std::cin >> a >> b; 30 | 31 | // 计算所需的天数 32 | long long d = (a + b - 1) / b; // 向上取整 33 | 34 | std::cout << d << std::endl; // 输出结果 35 | } 36 | 37 | return 0; 38 | } 39 | ``` 40 | -------------------------------------------------------------------------------- /problems/0176.切割正数.md: -------------------------------------------------------------------------------- 1 | 2 | # 176. 切割正数 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1254) 5 | 6 | 解题思路 7 | 8 | 动态规划数组: 9 | 10 | 使用一个动态规划数组 dp,其中 dp[i] 表示处理前 i 位数字时可以获得的最大和。 11 | 12 | 状态转移: 13 | 14 | 对于每一位 i,我们可以选择将最后的 1 位、2 位或 3 位数字作为一个切割部分,条件是这些数字必须不超过 100。 15 | 16 | 具体步骤如下: 17 | 18 | * 取最后 1 位:直接将前 i-1 位的最大和加上当前位的值。 19 | * 取最后 2 位:如果前两位的数字不超过 100,将 dp[i-2] 加上这两位的值。 20 | * 取最后 3 位:同样地,如果前三位的数字不超过 100,将 dp[i-3] 加上这三位的值。 21 | 22 | 初始化: 23 | 24 | 初始化 dp[0] = 0,表示没有数字时的和为 0。 25 | 26 | 最终结果: 输出 dp[n],其中 n 是输入数字的位数,表示整个数字切割后的最大和。 27 | 28 | 29 | ```CPP 30 | #include 31 | #include 32 | #include 33 | using namespace std; 34 | 35 | int main() { 36 | // 读取输入 37 | string x; 38 | cin >> x; 39 | 40 | int n = x.size(); 41 | vector dp(n + 1, 0); // dp[i] 表示前 i 位的最大和 42 | 43 | for (int i = 1; i <= n; ++i) { 44 | // 取最后 1 位 45 | dp[i] = dp[i - 1] + (x[i - 1] - '0'); 46 | 47 | // 取最后 2 位 48 | if (i >= 2) { 49 | int two_digit = (x[i - 2] - '0') * 10 + (x[i - 1] - '0'); 50 | if (two_digit <= 100) { 51 | dp[i] = max(dp[i], dp[i - 2] + two_digit); 52 | } 53 | } 54 | 55 | // 取最后 3 位 56 | if (i >= 3) { 57 | int three_digit = (x[i - 3] - '0') * 100 + (x[i - 2] - '0') * 10 + (x[i - 1] - '0'); 58 | if (three_digit <= 100) { 59 | dp[i] = max(dp[i], dp[i - 3] + three_digit); 60 | } 61 | } 62 | } 63 | 64 | // 输出结果 65 | cout << dp[n] << endl; 66 | 67 | return 0; 68 | } 69 | ``` 70 | 71 | 时间复杂度:O(n),其中 n 是数字的位数,因为我们只需遍历每一位进行常数时间的计算。 72 | 空间复杂度:O(n),用于存储动态规划数组 dp。 73 | -------------------------------------------------------------------------------- /problems/0177.学习语言.md: -------------------------------------------------------------------------------- 1 | 2 | # 177. 学习语言 3 | 4 | [题目链接](https://kamacoder.com/problempage.php?pid=1255) 5 | 6 | 这道题目要求我们计算小红和她的朋友们最少需要学习多少种语言,以便任何人都可以相互交流。交流的条件是两个人至少有一种共同的语言。我们可以使用并查集(Union-Find)来解决这个问题,具体步骤如下: 7 | 8 | 解题思路 9 | 10 | 定义数据结构: 11 | 12 | 使用一个并查集来跟踪每个人的语言关系。每个人可以看作一个节点,语言则用作连接这些节点的边。 13 | 14 | 输入处理: 15 | 16 | 首先读取参与者数量 N 和语言数量 M。 17 | 18 | 然后读取每个人所会的语言,并将这些信息存储在一个二维数组中。 19 | 20 | 建立语言与人的关系: 21 | 22 | 对于每种语言,将会这种语言的所有人合并到同一个集合中。这样,如果一个人能通过某种语言与另一个人交流,那么他们将属于同一个连通分量。 23 | 24 | 统计连通分量: 25 | 26 | 使用一个集合来记录不同的根节点(连通分量的代表)。通过对每个人进行查找,收集所有不同的根节点。 27 | 计算结果: 28 | 29 | 如果所有人都不会任何语言,则小红需要学习 N 种语言(每人学一种)。 30 | 31 | 否则,最少需要学习的语言数量为连通分量数量减去 1(因为小红已经会一种语言,其他人通过她可以联系到彼此)。 32 | 33 | 34 | ```CPP 35 | #include 36 | #include 37 | #include 38 | 39 | using namespace std; 40 | 41 | // 查找函数,带路径压缩 42 | int find(vector& parent, int x) { 43 | if (parent[x] != x) { 44 | parent[x] = find(parent, parent[x]); // 路径压缩 45 | } 46 | return parent[x]; 47 | } 48 | 49 | // 合并函数 50 | void unionSets(vector& parent, int x, int y) { 51 | int x_root = find(parent, x); 52 | int y_root = find(parent, y); 53 | if (x_root != y_root) { 54 | parent[y_root] = x_root; // 合并 55 | } 56 | } 57 | 58 | // 计算最少需要学习的语言数量 59 | int minimumLanguagesNeeded(int N, int M, const vector>& peopleLanguages) { 60 | vector parent(N); // 每个人的父节点 61 | int noLanguageCount = 0; // 不会任何语言的人数 62 | 63 | // 初始化 parent 数组 64 | for (int i = 0; i < N; ++i) { 65 | parent[i] = i; 66 | } 67 | 68 | // 记录每种语言有哪些人会 69 | vector> languagePeople(M); 70 | 71 | for (int i = 0; i < N; ++i) { 72 | const auto& languages = peopleLanguages[i]; 73 | if (languages.empty()) { 74 | noLanguageCount++; 75 | } 76 | for (int lang : languages) { 77 | languagePeople[lang - 1].push_back(i); // 记录每种语言的使用者 78 | } 79 | } 80 | 81 | // 合并会同一种语言的人的集合 82 | for (int lang = 0; lang < M; ++lang) { 83 | const auto& people = languagePeople[lang]; 84 | for (size_t i = 1; i < people.size(); ++i) { 85 | unionSets(parent, people[i], people[0]); 86 | } 87 | } 88 | 89 | // 统计连通分量数量 90 | set roots; 91 | for (int i = 0; i < N; ++i) { 92 | roots.insert(find(parent, i)); 93 | } 94 | 95 | int components = roots.size(); 96 | 97 | if (noLanguageCount == N) { 98 | // 所有人都不会任何语言 99 | return N; // 每人学一种 100 | } else { 101 | // 最少需要学习的语言数量为连通分量数量 - 1 102 | return components - 1; 103 | } 104 | } 105 | 106 | int main() { 107 | int N, M; 108 | cin >> N >> M; 109 | 110 | vector> peopleLanguages(N); 111 | for (int i = 0; i < N; ++i) { 112 | int k; 113 | cin >> k; 114 | peopleLanguages[i].resize(k); 115 | for (int j = 0; j < k; ++j) { 116 | cin >> peopleLanguages[i][j]; 117 | } 118 | } 119 | 120 | // 计算并输出结果 121 | int result = minimumLanguagesNeeded(N, M, peopleLanguages); 122 | cout << result << endl; 123 | 124 | return 0; 125 | } 126 | 127 | ``` 128 | -------------------------------------------------------------------------------- /problems/0178.寻找平均数.md: -------------------------------------------------------------------------------- 1 | # 178. 寻找平均数 2 | 3 | [题目链接](https://kamacoder.com/problempage.php?pid=1256) 4 | 5 | 我们需要解决的问题是计算一棵树中两节点之间路径上所有节点的权值平均数。为此,我们可以使用以下步骤: 6 | 7 | 构建树的表示:使用邻接表来表示树结构,同时存储每个节点的权值。 8 | 9 | 处理查询:对于每个查询,从节点 𝑥 到节点 y,我们需要找到路径上的所有节点,计算它们的权值和和数量,然后求平均值。 10 | 11 | 路径平均值计算:通过深度优先搜索(DFS),遍历路径并计算权值和与节点数量,最后利用模运算输出结果。 12 | 13 | 14 | 15 | 16 | ```CPP 17 | #include 18 | #include 19 | #include 20 | 21 | using namespace std; 22 | 23 | const int MOD = 1e9 + 7; // 定义模数 24 | 25 | // 计算 b 在 p 下的逆元 26 | long long mod_inverse(long long b, long long p) { 27 | long long result = 1; // 结果初始化为 1 28 | long long exponent = p - 2; // 计算 b^(p-2) 来得到逆元 29 | while (exponent > 0) { 30 | if (exponent % 2 == 1) { // 如果 exponent 是奇数 31 | result = (result * b) % p; // 更新结果 32 | } 33 | b = (b * b) % p; // b 自身平方 34 | exponent /= 2; // 指数减半 35 | } 36 | return result; 37 | } 38 | 39 | // 计算 a / b % p 40 | long long mod_fraction(long long a, long long b, long long p) { 41 | long long a_mod = a % p; // 取 a 的模 42 | long long b_inv = mod_inverse(b, p); // 计算 b 的逆元 43 | return (a_mod * b_inv) % p; // 返回 a * b 的逆元模 p 44 | } 45 | 46 | // 深度优先搜索(DFS)用于遍历路径并计算权值和 47 | void dfs(int parent, int current, int target, long long& cnt, int& k, 48 | const vector& weight, const unordered_map>& dic) { 49 | if (current == target) { // 如果当前节点是目标节点 50 | cout << mod_fraction(cnt, k, MOD) << endl; // 输出权值和的平均值 51 | return; // 返回 52 | } 53 | // 遍历当前节点的所有邻居 54 | for (int neighbor : dic.at(current)) { 55 | if (neighbor == parent) continue; // 如果邻居是父节点,跳过 56 | cnt += weight[neighbor]; // 加上邻居节点的权值 57 | k++; // 增加节点数量 58 | // 递归调用 DFS 继续向下遍历 59 | dfs(current, neighbor, target, cnt, k, weight, dic); 60 | cnt -= weight[neighbor]; // 回溯时减去邻居节点的权值 61 | k--; // 回溯时减少节点数量 62 | } 63 | } 64 | 65 | int main() { 66 | int n; 67 | cin >> n; // 输入节点数量 68 | 69 | vector weight(n + 1, 0); // 存储节点权值的数组(索引从 1 开始) 70 | for (int i = 1; i <= n; ++i) { 71 | cin >> weight[i]; // 输入每个节点的权值 72 | } 73 | 74 | unordered_map> dic; // 邻接表表示树的结构 75 | for (int i = 0; i < n - 1; ++i) { 76 | int u, v; 77 | cin >> u >> v; // 输入边的两个端点 78 | dic[u].push_back(v); // 将 v 加入 u 的邻居 79 | dic[v].push_back(u); // 将 u 加入 v 的邻居 80 | } 81 | 82 | int q; 83 | cin >> q; // 输入查询次数 84 | for (int i = 0; i < q; ++i) { 85 | int a, b; 86 | cin >> a >> b; // 输入查询的两个节点 87 | long long cnt = weight[a]; // 初始化路径上权值和为节点 a 的权值 88 | int k = 1; // 初始化路径上的节点数量为 1(包括 a) 89 | // 调用 DFS 进行路径查找和权值计算 90 | dfs(0, a, b, cnt, k, weight, dic); 91 | } 92 | 93 | return 0; 94 | } 95 | 96 | 97 | ``` 98 | -------------------------------------------------------------------------------- /problems/0184.米小游和蹦蹦史莱姆.md: -------------------------------------------------------------------------------- 1 | # 模拟 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1263) 3 | ## 思路分析 4 | 由于怪物一秒钟只能移动一格,且方向不会改变,所以怪物在第i秒的时候所处的格子只会在自身的左边i格或者右边i格。 5 | 6 | 如果怪物当前在j格,那么他在第i秒的位置只会在j+i格或者j-i格,1 <= j+i,j-i <= n. 7 | 8 | 所以用一个数组模拟每一秒每个格子是否有怪物存在即可。 9 | ## 复杂度分析: 10 | 1. 时间复杂度:O(n^2), 需要模拟每一秒每一个格子的怪物的情况。 11 | 2. 空间复杂度:O(n), 需要记录每一个格子存在的怪物的情况。 12 | # 代码实现 13 | ### Cpp 14 | ``` cpp 15 | #include 16 | using namespace std; 17 | const int N = 100005, mod = 1000000007; 18 | using LL = long long; 19 | using PIL = pair; 20 | using PII = pair; 21 | int T; 22 | int dir[]{1, 0, -1, 0, 1}; 23 | int main() { 24 | int n; 25 | cin >> n; 26 | int *has = new int[n + 1]{}; 27 | for (int i = 1; i <= n; ++i) cin >> has[i]; 28 | // 模拟每一秒 29 | for (int i = 1; i <= n; ++i) { 30 | bool *res = new bool[n + 1]{}; 31 | int cnt = 0; 32 | // 模拟每一个格子 33 | for (int j = 1; j <= n; ++j) { 34 | if (has[j] == 1) { 35 | // 表示往右跳i格, i秒只能跳i格 36 | if (j + i <= n) { 37 | res[j + i] = true; 38 | } 39 | } 40 | else { 41 | if (j - i >= 1) { 42 | res[j - i] = true; 43 | } 44 | } 45 | } 46 | for (int j = 1; j <= n; ++j) { 47 | if (res[j]) ++cnt; 48 | } 49 | cout << n - cnt << ' '; 50 | } 51 | return 0; 52 | } 53 | ``` 54 | 55 | ### Python 56 | ``` python 57 | n = int(input()) 58 | has = list(map(int, input().split())) 59 | for i in range(1, n + 1): 60 | ret = [0] * (n + 1) 61 | for j, x in enumerate(has): 62 | j += 1 63 | if x == 1: 64 | if j + i <= n: 65 | ret[j + i] = 1 66 | else: 67 | if j - i >= 1: 68 | ret[j - i] = 1 69 | cnt = sum(ret) 70 | print(n - cnt, end=' ') 71 | ``` 72 | -------------------------------------------------------------------------------- /problems/0185.米小游买手办.md: -------------------------------------------------------------------------------- 1 | # 模拟 + 队列 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1264) 3 | ## 思路分析 4 | 可以利用队列来模拟整个买手办的过程. 5 | 6 | 但是队列是先进先出的特性,无法让队列中任意位置的一个元素离开,所以需要额外的信息来记录队列中当前某一个元素是否有效。 7 | 8 | 本题可以通过记录每次入队时的时间(或者说此次事件的编号),来判断当前一个元素是否有效,每次购买时,检查当前队头元素所记录的时间是否和最后一次入队时间相同,不相同则为无效。如果无效,则直接出队即可。 9 | 10 | 队列中的人数同样也能用一个额外的变量来记录,入队事件则人数加一,离队事件则人数减一。 11 | ## 代码实现 12 | ### Cpp 13 | ``` cpp 14 | #include 15 | using namespace std; 16 | 17 | // 用来记录每个人的名字和进入队列的时间 18 | map nameToTime; 19 | // 用来记录每个人的名字以及购买了多少个手办 20 | map nameToCount; 21 | // 利用队列模拟, string为队列中的人的名字, int为这个人本次进入队列的时间 22 | queue> queueItems; 23 | // 记录当前队列中的人数 24 | int countItems; 25 | int n, m, t; 26 | 27 | void solve(int quantity) { 28 | while (!queueItems.empty()) { 29 | auto [name, time] = queueItems.front(); 30 | // 如果这个人最后进入队列的时间与他本次进入队列的时间不同,那么就说明他出去过,所以本次无效 31 | if (nameToTime[name] == time) { 32 | // 如果他在队列中是合法的,就可以计算他购买的数量 33 | nameToCount[name] += quantity; 34 | n -= quantity; 35 | return; 36 | } 37 | // 将这个人移出队列 38 | queueItems.pop(); 39 | } 40 | } 41 | 42 | // 输出结果 43 | void output() { 44 | for (auto [name, time] : nameToTime) { 45 | cout << name << ' ' << nameToCount[name] << endl; 46 | } 47 | } 48 | 49 | int main() { 50 | cin >> n >> m >> t; 51 | // 根据事件的不同类型进行输入,然后模拟计算答案 52 | for (int i = 0; i < t; i++) { 53 | int op; 54 | string name; 55 | cin >> op; 56 | if (op == 3) { 57 | int quantity; 58 | cin >> quantity; 59 | solve(quantity); 60 | if (n <= m) { 61 | solve(n); 62 | break; 63 | } 64 | } else if (op == 1) { 65 | cin >> name; 66 | nameToTime[name] = i; 67 | queueItems.push({name, i}); 68 | countItems++; 69 | } else if (op == 2) { 70 | cin >> name; 71 | nameToTime[name] = -1; 72 | countItems--; 73 | } else { 74 | // 输出当前队列中的人数 75 | cout << countItems << endl; 76 | } 77 | } 78 | output(); 79 | return 0; 80 | } 81 | ``` 82 | -------------------------------------------------------------------------------- /problems/0186.米小游和建木.md: -------------------------------------------------------------------------------- 1 | # 回溯+有序集合+二分查找 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1265) 3 | ## 思路分析 4 | 我们可以发现,一个节点的神实的数量与他的因子数有关,即他的所有子节点的编号存在多少个他的因子,那么他的神实数量就会对应的加多少. 5 | 6 | 反过来想,当前节点如果可以成为某个祖先节点的因子,就可以让这个祖先节点的神实数目加一. 7 | 8 | 也就是说在遍历到当前节点的时候,我们需要遍历当前节点的所有的祖先节点,如果当前节点的编号为x,那么如果他的祖先节点有任意编号为kx的节点的话,总神实的数量就可以加一, **因为x一定是kx的因子, 或者换句话说, 如果当前节点的子树有因子存在的话, 那么当前节点的编号一定是那个节点的编号的倍数**. 9 | 10 | 我们可以利用set存储当前节点的所有祖先节点的编号集合. 然后依次**遍历x, 2x, 3x, ... , kx. 直到kx > n结束**, 因为树中的节点编号为[1, n]. 11 | 12 | 由于set是有序的集合,我们可以实现二分查找快速找到一个数是不是存在集合中, 其次对于当前节点的所有祖先节点的数量最多**为floor(n/x)个**. 所以最多需要进行floor(n/x)次查找. 13 | 14 | 可以利用这种计算每个节点的贡献的思想去计算总神实的数量. 15 | ## 代码实现 16 | ### Cpp 17 | ``` cpp 18 | #include 19 | using namespace std; 20 | const int N = 100005; 21 | vectorg[N]; 22 | // 记录所有出现过的父节点 23 | unordered_set s; 24 | // 答案,即总神实的数目 25 | long long ans = 0; 26 | void dfs(int u, int p, int n) { 27 | // 将当前的节点的编号插入到集合中 28 | s.insert(u); 29 | // 依次计算当前节点的所有可能成为因子的祖先节点编号 30 | for (int i = u; i <= n; i += u) { 31 | if (s.find(i) != s.end()) { 32 | // 找到了, 更新答案 33 | ans += 1; 34 | } 35 | } 36 | // 继续遍历当前节点的所有子节点 37 | for (int v : g[u]) { 38 | if (v != p) { 39 | dfs(v, u, n); 40 | } 41 | } 42 | // 回溯, 将当前节点从集合中去掉 43 | s.erase(u); 44 | } 45 | 46 | int main() { 47 | int n, x; 48 | cin >> n >> x; 49 | // 建立树形结构 50 | for (int i = 0; i < n - 1; i++) { 51 | int u, v; 52 | cin >> u >> v; 53 | g[u].push_back(v); 54 | g[v].push_back(u); 55 | } 56 | dfs(x, 0, n); 57 | cout << ans << endl; 58 | return 0; 59 | } 60 | ``` 61 | ### Python 62 | ``` python 63 | n, x = map(int, input().split()) 64 | # 用来存储树结构 65 | graph = [[] for _ in range(n + 1)] 66 | 67 | # 建立树 68 | for i in range(n - 1): 69 | a, b = map(int, input().split()) 70 | graph[a].append(b) 71 | graph[b].append(a) 72 | 73 | # 存储父节点的编号 74 | parents = set() 75 | ans = 0 76 | def dfs(x: int, fa: int) -> None: 77 | global ans 78 | # 添加当前节点的编号 79 | parents.add(x) 80 | for i in range(x, n + 1, x): 81 | # 发现当前节点的倍数在父节点的集合里面,也就是说祖先节点有一个因子是这个节点 82 | if i in parents: 83 | # 将答案加一 84 | ans += 1 85 | # 遍历其当前节点的子节点 86 | for t in graph[x]: 87 | # 因为是双向存储的,所以接下来遍历的节点不能是自己的父亲节点 88 | if t != fa: 89 | dfs(t, x) 90 | # 将当前节点移除集合 91 | parents.remove(x) 92 | 93 | # 由于树的编号是从1开始的,所以用0表示根节点的父亲节点,不会造成冲突 94 | dfs(x, 0) 95 | print(ans) 96 | ``` 97 | -------------------------------------------------------------------------------- /problems/0187.小欧新建文件夹.md: -------------------------------------------------------------------------------- 1 | # 哈希表模拟 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1266) 3 | ## 思路分析 4 | 我们只需要在创建这个文件创建之前判断目前已经出现过几次这个文件名。当出现的次数大于等于1的时候就可以在后面接上对应的数字即可。 5 | 6 | 用一个哈希表记录每个文件出现的次数。 7 | ## 复杂度分析: 8 | 1. 时间复杂度:O(n), 需要遍历每一个文件夹名字。 9 | 2. 空间复杂度:O(n), 哈希表使用的空间。 10 | ## 代码实现 11 | ### Cpp 12 | ``` cpp 13 | #include 14 | using namespace std; 15 | const int N = 100005, mod = 1000000007; 16 | using LL = long long; 17 | using PIL = pair; 18 | using PII = pair; 19 | int T; 20 | int dir[]{1, 0, -1, 0, 1}; 21 | int main() { 22 | int n; 23 | string s; 24 | cin >> n; 25 | // 记录每个文件出现的次数 26 | unordered_map name2cnt; 27 | while (n--) { 28 | cin >> s; 29 | int &t = name2cnt[s]; 30 | cout << s; 31 | if (t) cout << '(' << t << ')'; 32 | cout << endl; 33 | ++t; 34 | } 35 | return 0; 36 | } 37 | ``` 38 | 39 | ### Python 40 | ``` python 41 | from collections import defaultdict 42 | n = int(input()) 43 | # 记录文件出现的次数 44 | name2cnt = defaultdict(int) 45 | for _ in range(n): 46 | s = input() 47 | print(s, end='') 48 | t = name2cnt[s] 49 | if t: 50 | print(f'({name2cnt[s]})', end='') 51 | print('') 52 | name2cnt[s] += 1 53 | ``` 54 | 55 | ### Java 56 | ``` java 57 | import java.io.*; 58 | import java.util.*; 59 | 60 | public class Main { 61 | public static void main(String[] args) { 62 | // 读取输入 63 | BufferedReader read = new BufferedReader(new InputStreamReader(System.in)); 64 | // 记录出现的次数 65 | Map name2cnt = new HashMap<>(); 66 | try { 67 | int n = Integer.parseInt(read.readLine()); 68 | for (int i = 0; i < n; ++i) { 69 | String s = read.readLine(); 70 | int t = name2cnt.getOrDefault(s, 0); 71 | System.out.print(s); 72 | if (t != 0) System.out.print("(" + t + ")"); 73 | System.out.println(""); 74 | name2cnt.put(s, t + 1); 75 | } 76 | } catch(IOException e) { 77 | 78 | } 79 | } 80 | } 81 | ``` 82 | -------------------------------------------------------------------------------- /problems/0188.小欧的等差数列.md: -------------------------------------------------------------------------------- 1 | # 数学 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1267) 3 | ## 思路分析 4 | 这是一道与数学有关的题。 5 | 我们将分为四种情况讨论: 6 | 1. 首项为奇数,公差为奇数: 7 | 8 | 在这种情况下,我们得到的序列是这样的:奇,偶,奇,偶,……,奇,偶。每个相邻的奇偶之间有(d-1)个数,d为公差。由于只有**奇数加奇数**和**偶数加偶数**才会等于偶数。所以这样的序列我们只能用每间隔的两个数相加形成偶数,如数列为a, a+d, a+2d, ..., a+(n-1)d. 而我们只能用**第一项和第三项相加=>(a+a+2d)/2=a+d**.发现这个数等于第二项,对于后面的推理同样如此。**如(a+d+a+3d)/2=a+2d**. 所以对于这种情况,我们发现答案就是n。 9 | 10 | 2. 首项为偶数,公差为奇数: 11 | 12 | 对于这种情况,我们得到的序列为:偶,奇,偶,奇,……,偶,奇。与上面的分析类似,我们同样无法产生新的数。答案同样是n。 13 | 14 | 3. 首项为奇数,公差为偶数: 15 | 16 | 对于这种情况,得到的序列是全是奇数。***我们把每两个相邻的奇数相加除以2得到一个新的数列,也是等差数列***,对于这个数列而言首项没变,只有公差变为了原来的一半,这就是一个子问题,继续按照这种方式求解,直到d变成奇数。同时我们要累积每次能新增的数的数量。而这个数量就是当前的数的数量减一。证明如下:*如果一开始我们有n个数,那么第一次执行这种操作时,是把每两个相邻的奇数合并,会合并n-1次,每次新增一个数。会新增n-1个数,数列的长度变为n+n-1,然后,根据子问题计算,下次新增数的时候,会新增(n+n-1)-1个数。* 17 | 18 | 4. 首项为偶数,公差为偶数: 19 | 20 | 我们会得到全是偶数的数列,推导过程同上面。 21 | ## 复杂度分析: 22 | 1. 时间复杂度:O(log(d)), d为公差。 23 | 2. 空间复杂度:O(1), 使用了常量的空间。 24 | ## 代码实现 25 | 26 | ### Cpp 27 | ``` cpp 28 | #include 29 | using namespace std; 30 | const int N = 100005, mod = 1000000007; 31 | using LL = long long; 32 | using PIL = pair; 33 | using PII = pair; 34 | int T; 35 | int dir[]{1, 0, -1, 0, 1}; 36 | int main() { 37 | int n, a, d; 38 | cin >> n >> a >> d; 39 | while (d % 2 == 0) { 40 | n += n - 1; 41 | d >>= 1; 42 | } 43 | cout << n << endl; 44 | return 0; 45 | } 46 | ``` 47 | 48 | ### Python 49 | ``` python 50 | n, _, d = map(int, input().split()) 51 | cnt = n 52 | while d % 2 == 0: 53 | cnt += cnt - 1 54 | d >>= 1 55 | print(cnt) 56 | ``` 57 | 58 | ### Java 59 | ``` java 60 | import java.io.*; 61 | import java.util.*; 62 | 63 | public class Main { 64 | public static void main(String[] args) { 65 | Scanner read = new Scanner(System.in); 66 | int n = read.nextInt(); 67 | int a = read.nextInt(); 68 | int d = read.nextInt(); 69 | while (d % 2 == 0) { 70 | n += n - 1; 71 | d >>= 1; 72 | } 73 | System.out.println(n); 74 | } 75 | } 76 | ``` 77 | -------------------------------------------------------------------------------- /problems/0189.小欧的弹球.md: -------------------------------------------------------------------------------- 1 | # 这是一道数学与几何结合的题 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1268) 3 | ## 思路分析 4 | 为了方便讲解,下面我将题目等价替换为光线反射问题。 5 | 6 | 首先根据光的反射原理,设有A(x1, y1), B(x2, y2)两点,我们要求的是从A点朝Y轴发射一条光线,使其经过Y轴的反射到X轴,再经过X轴的反射,最后到达B点。 7 | 8 | 假设A点与Y轴的反射点为(0,b),与X轴的反射点为(c,0)。与Y轴的反射点的夹角的锐角部分为α,那么其与法线的夹角为β,而α+β=90°,所以可以知道在经过X轴反射过后光线与X轴的夹角也是β,由于题目保证数据存在正整数解,所以这两条光线是平行的,根据三角函数公式可知: 9 | 10 | 1. tanβ=y2/(x1-c) 11 | 2. tanβ=(y1-b)/x1 12 | 3. tanβ=b/c 13 | 14 | 联立上面三个方程可以求得b = (x2 * y1 - x1 * y2) / (x1 + x2) 15 | 16 | 所求答案即为(0.00,b)。 17 | 18 | ***注意:使用强类型语言的要记得用double或者longlong类型,否则可能出现精度错误的问题。*** 19 | ## 复杂度分析: 20 | 1. 时间复杂度:O(1), 利用公式计算。 21 | 2. 空间复杂度:O(1), 使用了常量的空间。 22 | ## 代码实现 23 | 24 | ### Cpp 25 | ``` cpp 26 | #include 27 | using namespace std; 28 | const int N = 100005, mod = 1000000007; 29 | using LL = long long; 30 | using PIL = pair; 31 | using PII = pair; 32 | int T; 33 | int dir[]{1, 0, -1, 0, 1}; 34 | int main() { 35 | // 注意要用double,否则可能出现精度错误 36 | double x1, y1, x2, y2; 37 | cin >> x1 >> y1 >> x2 >> y2; 38 | double ans = (x2 * y1 - x1 * y2) / (x1 + x2); 39 | printf("0.00 %.02lf", ans); 40 | return 0; 41 | } 42 | ``` 43 | 44 | ### Python 45 | 46 | ``` python 47 | x1, y1, x2, y2 = map(int, input().split()) 48 | b = (x2 * y1 - x1 * y2) / (x1 + x2) 49 | print(f'0.00 {b:.02f}') 50 | ``` 51 | ### Java 52 | ``` java 53 | import java.io.*; 54 | import java.util.*; 55 | 56 | public class Main { 57 | public static void main(String[] args) { 58 | Scanner read = new Scanner(System.in); 59 | double x1 = read.nextDouble(); 60 | double y1 = read.nextDouble(); 61 | double x2 = read.nextDouble(); 62 | double y2 = read.nextDouble(); 63 | double b = (x2 * y1 - x1 * y2) / (x1 + x2); 64 | System.out.print("0.00 "); 65 | System.out.printf("%.02f\n", b); 66 | } 67 | } 68 | ``` 69 | -------------------------------------------------------------------------------- /problems/0192.游游的you子串.md: -------------------------------------------------------------------------------- 1 | # 模拟+计数 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1271) 3 | ## 思路分析 4 | 我们用三个变量分别记录字符'y'、'o'、'u'出现的次数即可。最后的答案是这三个字符出现次数的最小值。 5 | ### 复杂度分析 6 | 1. 时间复杂度:O(n) 7 | 2. 空间复杂度:O(1),可以做到O(1),下面的java写法就是O(1). 8 | ## 代码实现 9 | ### Cpp 10 | ``` cpp 11 | #include 12 | using namespace std; 13 | const int N = 100005, MOD = 1000000007; 14 | using LL = long long; 15 | using PIL = pair; 16 | using PII = pair; 17 | int t, ans = 0; 18 | int main() { 19 | string s; 20 | cin >> s; 21 | int cnt[128]{}; 22 | for (char c : s) ++cnt[c]; 23 | cout << min({cnt['y'], cnt['o'], cnt['u']}) << endl; 24 | return 0; 25 | } 26 | ``` 27 | ### Python 28 | ``` python 29 | s = input() 30 | cnt = Counter(s) 31 | print(min(cnt['y'], cnt['o'], cnt['u'])) 32 | ``` 33 | ### Java 34 | ``` java 35 | import java.util.Scanner; 36 | 37 | public class Main { 38 | public static void main(String[] args) { 39 | Scanner scanner = new Scanner(System.in); 40 | // 读取输入字符串 41 | String s = scanner.nextLine(); 42 | // 初始化计数器 43 | int countY = 0, countO = 0, countU = 0; 44 | // 单次遍历字符串,统计 'y', 'o', 'u' 的出现次数 45 | for (char c : s.toCharArray()) { 46 | if (c == 'y') { 47 | countY++; 48 | } else if (c == 'o') { 49 | countO++; 50 | } else if (c == 'u') { 51 | countU++; 52 | } 53 | } 54 | // 获取 'y', 'o', 'u' 的最小频率 55 | int minCount = Math.min(countY, Math.min(countO, countU)); 56 | // 打印结果 57 | System.out.println(minCount); 58 | } 59 | } 60 | ``` -------------------------------------------------------------------------------- /problems/0196.小美的MT.md: -------------------------------------------------------------------------------- 1 | # 模拟+计数 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1275) 3 | ## 思路分析 4 | 用一个变量cnt记录'M', 'T'出现的次数, 然后剩下可操作的字符数量为n-cnt, n为字符串的长度, 剩下的操作次数为k, 我们取最小值加上即可. 5 | ## 复杂度分析 6 | 1. 时间复杂度: O(n) 7 | 2. 空间复杂度: O(1) 8 | ## 代码实现 9 | ### Cpp 10 | ``` cpp 11 | #include 12 | #define endl '\n'; 13 | const int N = 10005, MOD = 1000000007; 14 | using namespace std; 15 | using LL = long long; 16 | using PIL = pair; 17 | using PII = pair; 18 | int t, ans = 0; 19 | int main() { 20 | int n, k; 21 | cin >> n >> k; 22 | string s; 23 | cin >> s; 24 | int cnt = 0; 25 | for (char c : s) { 26 | if (c == 'M' || c == 'T') ++cnt; 27 | } 28 | ans = cnt + min(k, n - cnt); 29 | cout << ans << endl; 30 | return 0; 31 | } 32 | ``` 33 | ### Python 34 | ``` python 35 | n, k = map(int, input().split()) 36 | s = input() 37 | cnt = sum(1 for c in s if c == 'M' or c == 'T') 38 | ans = cnt + min(k, n - cnt) 39 | print(ans) 40 | ``` 41 | ### Java 42 | ``` java 43 | import java.util.Scanner; 44 | 45 | public class Main { 46 | public static void main(String[] args) { 47 | Scanner sc = new Scanner(System.in); 48 | int n = sc.nextInt(); 49 | int k = sc.nextInt(); 50 | sc.nextLine(); // Consume the newline character 51 | String s = sc.nextLine(); 52 | 53 | int cnt = 0; 54 | for (char c : s.toCharArray()) { 55 | if (c == 'M' || c == 'T') { 56 | cnt++; 57 | } 58 | } 59 | 60 | int ans = cnt + Math.min(k, n - cnt); 61 | System.out.println(ans); 62 | } 63 | } 64 | ``` 65 | -------------------------------------------------------------------------------- /problems/0202.小红切字符串.md: -------------------------------------------------------------------------------- 1 | # 前缀和 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1281) 3 | ## 思路分析 4 | 求出整个串的元音字母的前缀和, 这样我们就可以在O(1)的时间求出任意一个区间的元音和辅音字母的数量. 然后枚举以每一个下标i为分界线, 将字符串分为两个部分[0,i]和[i+1, n-1], 并在O(1)的时间求出元音和辅音字母的数量, 然后计算他们的差值绝对值, 并比较两个区间的差值绝对值是否相等. 5 | ## 复杂度分析 6 | 1. 时间复杂度: O(n). 7 | 2. 空间复杂度: O(n). 8 | ## 代码实现 9 | ### Cpp 10 | ``` cpp 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | int main() { 18 | string s; 19 | cin >> s; // 输入字符串 20 | int n = s.length(); 21 | int ans = 0; 22 | 23 | // 元音字母集合 24 | int vowels[128]{}; 25 | vowels['a'] = vowels['e'] = vowels['i'] = vowels['o'] = vowels['u'] = 1; 26 | 27 | // 前缀和数组 28 | vector prefix(n + 1, 0); 29 | 30 | // 计算元音字母的前缀和 31 | for (int i = 0; i < n; ++i) { 32 | prefix[i + 1] = prefix[i] + vowels[s[i]]; 33 | } 34 | 35 | // 以 i 为分界点,计算左右差值并比较 36 | for (int i = 0; i < n - 1; ++i) { 37 | int ta = i + 1, ay = prefix[i + 1]; // 前半段元音数量和总长度 38 | int tb = n - (i + 1), by = prefix[n] - prefix[i + 1]; // 后半段元音数量和总长度 39 | 40 | // 判断左右两边的差值是否相等 41 | if (abs(ta - 2 * ay) == abs(tb - 2 * by)) { 42 | ans++; 43 | } 44 | } 45 | 46 | // 输出答案 47 | cout << ans << endl; 48 | return 0; 49 | } 50 | ``` 51 | ### Python 52 | ``` python 53 | s = input() 54 | n = len(s) 55 | ans = 0 56 | abs = lambda x: x if x >= 0 else -x 57 | vowels = set('aeiou') 58 | prefix = [0] * (n + 1) 59 | # 计算元音字母的前缀和 60 | for i in range(n): 61 | prefix[i + 1] = prefix[i] + (s[i] in vowels) 62 | # 以i为分界点,左边元音字母的个数与辅音字母的个数差等于右边元音字母的个数与辅音字母的个数差 63 | for i in range(n-1): 64 | # 前半段元音字母的数量, 以及字母的总数 65 | ay, ta = prefix[i + 1], i + 1 66 | # 后半段元音字母的数量, 以及字母的总数 67 | by, tb = prefix[n] - prefix[i + 1], n - (i + 1) 68 | if abs(ta - ay - ay) == abs(tb - by - by): 69 | ans += 1 70 | print(ans) 71 | ``` 72 | ### Java 73 | ``` java 74 | import java.util.*; 75 | 76 | public class Main { 77 | public static void main(String[] args) { 78 | Scanner sc = new Scanner(System.in); 79 | String s = sc.next(); // 输入字符串 80 | int n = s.length(); 81 | int ans = 0; 82 | 83 | // 元音字母集合 84 | Set vowels = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u')); 85 | 86 | // 前缀和数组 87 | int[] prefix = new int[n + 1]; 88 | 89 | // 计算元音字母的前缀和 90 | for (int i = 0; i < n; i++) { 91 | prefix[i + 1] = prefix[i] + (vowels.contains(s.charAt(i)) ? 1 : 0); 92 | } 93 | 94 | // 以 i 为分界点,计算左右差值并比较 95 | for (int i = 0; i < n - 1; i++) { 96 | int ta = i + 1, ay = prefix[i + 1]; // 前半段元音数量和总长度 97 | int tb = n - (i + 1), by = prefix[n] - prefix[i + 1]; // 后半段元音数量和总长度 98 | 99 | // 判断左右两边的差值是否相等 100 | if (Math.abs(ta - 2 * ay) == Math.abs(tb - 2 * by)) { 101 | ans++; 102 | } 103 | } 104 | 105 | // 输出答案 106 | System.out.println(ans); 107 | } 108 | } 109 | ``` -------------------------------------------------------------------------------- /problems/0205.小A的授权请求.md: -------------------------------------------------------------------------------- 1 | # dfs+有序集合 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1284) 3 | ## 思路分析 4 | 我们自顶向下遍历这棵树,每次经过一个节点的时候,就将这个节点的编号和技术能力放到multiset(有序集合中),里面存放的是```pair```表示这个节点的**技术能力**和他的**编号**。由于我们是自顶向下遍历整棵树的,那么对于后面的节点来说,multiset中已经有的节点就是他的所有的领导,我们通过**二分查找找到与他技术能力最近的两个领导的编号**。为什么是两个?请看下面的分析: 5 | 6 | 首先,题目要求的是找到绝对差值最小的领导,那么要么我们要找的领导的技术能力是>=当前节点的,要么是<的。也就是说找到技术能力>=(最小的大于等于)和<(最大的小于)的两位领导的编号,最终比较哪一个离我们当前的节点的技术能力最近,就选择哪个领导作为授权的领导,如果说这两个领导技术能力的差值绝对值一样的近,**那就选编号最小的领导**。这里又是为什么? 7 | 8 | 根据题目给出的隐藏条件,**第i个数 fi (i < fi ≤ n)代表编号为i的员工的直属领导是fi**,这说明了这个人的编号一定小于他的领导的编号,也就是说每个人的编号都小于他的所有上级的编号。在树中体现为祖先节点的编号一定大于子树的所有节点的编号。那么离当前的人组织架构越近的领导,他的编号也就越小。 9 | 10 | 这里需要注意,在找小于当前节点技术能力的节点时,也是一次二分查找的过程,(**因为可能有多个小于当前节点技术能力的领导,而我们关心的是最小的编号的那一个**),上面的分析证明了这一点,详细实现见代码的注释。 11 | ## 复杂度分析 12 | 1. 时间复杂度:O(nlog(n)) 13 | 2. 空间复杂度:O(n) 14 | ## 代码实现 15 | ### Cpp 16 | ``` cpp 17 | #include 18 | using namespace std; 19 | const int N = 10005, MOD = 1000000007; 20 | using LL = long long; 21 | using PIL = pair; 22 | using PII = pair; 23 | int main() { 24 | int n; 25 | set parents; 26 | cin >> n; 27 | int *father = new int[n + 1]; // 记录每个人的上级节点编号 28 | int *thinks = new int[n + 1]; // 记录每个编号的人的思维值 29 | // 处理输入 30 | for (int i = 1; i < n; ++i) scanf("%d", father+i); 31 | for (int i = 1; i <= n; ++i) scanf("%d", thinks+i); 32 | vector> graph(n + 1); 33 | // 构建树形结构(有向) 34 | for (int i = 1; i < n; ++i) { 35 | graph[father[i]].push_back(i); 36 | } 37 | vector ans(n); // 记录答案 38 | auto dfs = [&](auto&& dfs, int x) -> void { 39 | // 存放的是领导的思维和领导的编号 40 | parents.emplace(thinks[x], x); 41 | for (int t : graph[x]) { 42 | int curT = thinks[t]; 43 | auto it = parents.lower_bound({curT, 0}); 44 | // 当前的人的思维值为curT,大于等于他的思维值的领导的思维值为nv1, 编号为nn1,小于的为nv2,编号为nn2. 45 | int nv1 = MOD, nv2 = -MOD, nn1 = 0, nn2 = 0; 46 | if (it != parents.end()) nv1 = it -> first, nn1 = it -> second; 47 | // 去找等于他前面那一个人的思维值的最小编号(即小于他的) 48 | if (it != parents.begin()) { 49 | it = parents.lower_bound({prev(it) -> first, 0}); 50 | nv2 = it -> first; 51 | nn2 = it -> second; 52 | } 53 | int dn1 = nv1 - curT; // 最接近的两的领导的思维值与他的差值 54 | int dn2 = curT - nv2; 55 | // printf("t=%d dn1=%d dn2=%d, nn1=%d, nn2=%d\n", t, dn1, dn2, nn1, nn2); 56 | // 越接近t的领导编号一定越小 57 | if (dn1 < dn2) ans[t] = nn1; 58 | else if (dn2 < dn1) ans[t] = nn2; 59 | else { 60 | if (nn1 < nn2) ans[t] = nn1; 61 | else ans[t] = nn2; 62 | } 63 | dfs(dfs, t); 64 | } 65 | parents.erase({thinks[x], x}); 66 | }; 67 | // 从CEO开始遍历, CEO的编号为n-1 68 | dfs(dfs, n); 69 | for (int i = 1; i < n - 1; ++i) printf("%d ", ans[i]); 70 | printf("%d", ans.back()); 71 | return 0; 72 | } 73 | ``` -------------------------------------------------------------------------------- /problems/0206.开幕式排练.md: -------------------------------------------------------------------------------- 1 | # 排序 + 贪心 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1285) 3 | ## 思路分析 4 | 由于题目想要求相邻的两个人的身高差要尽可能的小。那么我们很容易想到,对数组进行排序,然后身高差相近的人就在一起了,但是题目说所有人最终会形成一个环,如果直接按照排序过后的结果进行排列,那么最终最高的人和最低的人一定会站在一起。那么最终的答案就是数组最大最小值的差。很明显这个结果是不对的。那该怎么做? 5 | 6 | 同样的,我们仍然对数组进行排序,考虑将每一个人作为队列的起始位置,然后每隔一个下标就安排一个人进入当前的队列。那么最终形成的队列就是一座拱形的山的形状。如:2, 3, 5, 4, 2, 1。这个队列先呈现非递减的趋势然后到达峰顶,再呈现非递增的趋势。 7 | 8 | 那么这个时候,答案就是排序完所有奇数下标的差,和偶数下标的差的最大值。因为我们要将所有的**奇数下标排在前面,然后所有的偶数下标排在后面。或者反之**。 9 | ## 复杂度分析 10 | 1. 时间复杂度:O(nlog(n)),瓶颈在于排序。 11 | 2. 空间复杂度:O(1)。不计输入输出。 12 | ## 代码实现 13 | ### Cpp 14 | ``` cpp 15 | #include 16 | using namespace std; 17 | const int N = 10005, MOD = 1000000007; 18 | using LL = long long; 19 | using PIL = pair; 20 | using PII = pair; 21 | int t, ans = 0; 22 | int main() { 23 | int n; 24 | cin >> n; 25 | int *nums = new int[n]; 26 | for (int i = 0; i < n; ++i) cin >> nums[i]; 27 | sort(nums, nums + n); 28 | int ans = nums[1] - nums[0]; 29 | for (int i = 0, pre = nums[0]; i < n; pre = nums[i], i += 2) { 30 | ans = max(ans, nums[i] - pre); 31 | } 32 | for (int i = 1, pre = nums[1]; i < n; pre = nums[i], i += 2) { 33 | ans = max(ans, nums[i] - pre); 34 | } 35 | cout << ans << '\n'; 36 | return 0; 37 | } 38 | ``` 39 | ### Python 40 | ``` python 41 | import sys 42 | input = sys.stdin.read 43 | 44 | def main(): 45 | # 使用 sys.stdin.read 提高输入速度 46 | data = input().split() 47 | # 读取数组大小 48 | n = int(data[0]) 49 | nums = list(map(int, data[1:])) 50 | # 对数组进行排序 51 | nums.sort() 52 | # 初始化最大差值 53 | ans = nums[1] - nums[0] 54 | # 计算偶数索引的最大差值 55 | pre = nums[0] 56 | for i in range(0, n, 2): 57 | ans = max(ans, nums[i] - pre) 58 | pre = nums[i] 59 | # 计算奇数索引的最大差值 60 | pre = nums[1] 61 | for i in range(1, n, 2): 62 | ans = max(ans, nums[i] - pre) 63 | pre = nums[i] 64 | # 输出结果 65 | print(ans) 66 | 67 | if __name__ == "__main__": 68 | main() 69 | ``` 70 | ### Java 71 | ``` java 72 | import java.io.*; 73 | import java.util.*; 74 | 75 | public class Main { 76 | public static void main(String[] args) throws IOException { 77 | // 使用 BufferedReader 和 PrintWriter 实现快速输入输出 78 | BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 79 | PrintWriter out = new PrintWriter(System.out); 80 | // 读取数组大小 81 | int n = Integer.parseInt(br.readLine()); 82 | int[] nums = new int[n]; 83 | // 读取数组元素 84 | String[] input = br.readLine().split(" "); 85 | for (int i = 0; i < n; i++) { 86 | nums[i] = Integer.parseInt(input[i]); 87 | } 88 | // 对数组进行排序 89 | Arrays.sort(nums); 90 | // 初始化最大差值 91 | int ans = nums[1] - nums[0]; 92 | // 计算偶数索引的最大差值 93 | for (int i = 0, pre = nums[0]; i < n; i += 2) { 94 | ans = Math.max(ans, nums[i] - pre); 95 | pre = nums[i]; 96 | } 97 | // 计算奇数索引的最大差值 98 | for (int i = 1, pre = nums[1]; i < n; i += 2) { 99 | ans = Math.max(ans, nums[i] - pre); 100 | pre = nums[i]; 101 | } 102 | // 输出结果 103 | out.println(ans); 104 | out.flush(); 105 | } 106 | } 107 | ``` 108 | -------------------------------------------------------------------------------- /problems/0208.小红的二叉树构造.md: -------------------------------------------------------------------------------- 1 | # 模拟 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1287) 3 | ## 思路分析 4 | 从二叉树的叶子结点开始,将所有的叶子结点权值赋为1,每往上一层,权值乘2。那最上面一层的权值,即根节点的为2^(n-1),由于每一层的节点的权值和一样,二叉树一共有n层,所以最终的答案为n\*(2^(n-1))。 5 | 为什么叶子节点每个节点的权值都为1,因为题目要求的是二叉树最小的可能权值和,所以叶子结点的权值一定是1。 6 | ## 复杂度分析 7 | 1. 时间复杂度:O(1); 8 | 2. 空间复杂度:O(1). 9 | ## 代码实现 10 | ### Cpp 11 | ``` cpp 12 | #include 13 | #define endl '\n' 14 | const int N = 100005, MOD = 1000000007; 15 | using namespace std; 16 | using LL = long long; 17 | using PIL = pair; 18 | using PII = pair; 19 | int t = 0, dir[]{1, 0, -1, 0, 1}; 20 | LL ans = 0; 21 | int main() { 22 | int n; 23 | cin >> n; 24 | cout << n * (1 << n - 1) << endl; 25 | return 0; 26 | } 27 | ``` 28 | ### Python 29 | ``` python 30 | n = int(input()) 31 | print(n * (1 << n - 1)) 32 | ``` 33 | ### Java 34 | ``` java 35 | import java.util.Scanner; 36 | 37 | public class Main { 38 | public static void main(String[] args) { 39 | Scanner scanner = new Scanner(System.in); 40 | int n = scanner.nextInt(); 41 | scanner.close(); 42 | 43 | System.out.println(n * (1 << (n - 1))); 44 | } 45 | } 46 | ``` -------------------------------------------------------------------------------- /problems/0210.最长合法子串长度.md: -------------------------------------------------------------------------------- 1 | # 二分 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1290) 3 | ## 思路分析 4 | 看到题目中的关键字“最小的最大值”,我们直接就想到二分。既然使用二分查找答案,那么就需要知道二分的上下界,根据题意不难发现,上界为字符串的长度,而下界为0,即所有的数字全是0。 5 | 6 | 每次二分的过程遍历一次字符串,如果当前的连续1的数量超过了mid,那么就需要将最后一个1变为0,记录一次操作次数,看最后的操作次数是否小于等于k即可。 7 | ## 复杂度分析 8 | 1. 时间复杂度:O(nlog(n)); 9 | 2. 空间复杂度:O(1),不计输入输出。 10 | ## 代码实现 11 | ### Cpp 12 | ``` cpp 13 | #include 14 | #include 15 | 16 | using namespace std; 17 | 18 | string s; 19 | int k; 20 | 21 | bool check(int x) { 22 | int cnt = 0, j = -1; 23 | for (int i = 0; i < s.size(); i++) { 24 | if (s[i] == '1' && i - j <= x) { 25 | continue; 26 | } 27 | if (s[i] == '1') { 28 | cnt++; 29 | } 30 | j = i; 31 | } 32 | return cnt <= k; 33 | } 34 | 35 | int main() { 36 | string input; 37 | cin >> input; 38 | 39 | size_t pos = input.find(','); 40 | s = input.substr(1, pos - 2); // 去掉前后的引号 41 | k = stoi(input.substr(pos + 1)); 42 | 43 | int l = 0, r = s.size(), mid; 44 | while (l <= r) { 45 | mid = (l + r) >> 1; 46 | if (check(mid)) { 47 | r = mid - 1; 48 | } else { 49 | l = mid + 1; 50 | } 51 | } 52 | cout << l << endl; 53 | return 0; 54 | } 55 | ``` 56 | ### Python 57 | ``` python 58 | s, k = input().split(',') 59 | s = s[1:-1] 60 | k = int(k) 61 | l, r, mid = 0, len(s), 0 62 | 63 | def check(x): 64 | cnt, j = 0, -1 65 | for i in range(len(s)): 66 | if s[i] == '1' and i - j <= x: 67 | continue 68 | if s[i] == '1': 69 | cnt += 1 70 | j = i 71 | return cnt <= k 72 | 73 | while l <= r: 74 | mid = l + r >> 1 75 | if check(mid): 76 | r = mid - 1 77 | else: 78 | l = mid + 1 79 | print(l) 80 | ``` 81 | ### Java 82 | ``` java 83 | import java.util.Scanner; 84 | 85 | public class Main { 86 | static String s; 87 | static int k; 88 | 89 | static boolean check(int x) { 90 | int cnt = 0, j = -1; 91 | for (int i = 0; i < s.length(); i++) { 92 | if (s.charAt(i) == '1' && i - j <= x) { 93 | continue; 94 | } 95 | if (s.charAt(i) == '1') { 96 | cnt++; 97 | } 98 | j = i; 99 | } 100 | return cnt <= k; 101 | } 102 | 103 | public static void main(String[] args) { 104 | Scanner scanner = new Scanner(System.in); 105 | String input = scanner.next(); 106 | scanner.close(); 107 | 108 | String[] parts = input.split(","); 109 | s = parts[0].substring(1, parts[0].length() - 1); // 去掉前后的引号 110 | k = Integer.parseInt(parts[1]); 111 | 112 | int l = 0, r = s.length(), mid; 113 | while (l <= r) { 114 | mid = (l + r) >> 1; 115 | if (check(mid)) { 116 | r = mid - 1; 117 | } else { 118 | l = mid + 1; 119 | } 120 | } 121 | System.out.println(l); 122 | } 123 | } 124 | ``` -------------------------------------------------------------------------------- /problems/0211.小苯的文章浏览.md: -------------------------------------------------------------------------------- 1 | # 哈希表 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1291) 3 | ## 思路分析 4 | 利用集合存储每一个出现的字符串,如果这个字符串之前出现过,那就不将他插入这个集合,否则就将其插入集合中并添加到记录答案的数组中,最后按顺序打印数组即可。最好是将答案记录到数组中统一输出,避免频繁的调用输入输出的函数。 5 | ## 复杂度分析 6 | 1. 时间复杂度:O(n); 7 | 2. 空间复杂度:O(n|C|), C为常数,即字符串的长度。 8 | ## 代码实现 9 | ### Cpp 10 | ``` cpp 11 | #include 12 | #define endl '\n' 13 | const int N = 100005, MOD = 1000000007; 14 | using namespace std; 15 | using LL = long long; 16 | using PIL = pair; 17 | using PII = pair; 18 | int t = 0, dir[]{1, 0, -1, 0, 1}; 19 | int main() { 20 | int n; 21 | string s; 22 | unordered_set us; 23 | vector ans; 24 | cin >> n; 25 | for (int i = 0; i < n; ++i) { 26 | cin >> s; 27 | if (us.count(s)) continue; 28 | us.emplace(s); 29 | ans.emplace_back(s); 30 | } 31 | for (string &t : ans) cout << t << endl; 32 | return 0; 33 | } 34 | ``` 35 | ### Python 36 | ``` python 37 | import sys 38 | 39 | def main(): 40 | # 读取所有行,加快输入速度 41 | data = sys.stdin.read().splitlines() 42 | if not data: 43 | return 44 | n = int(data[0]) 45 | seen = set() 46 | output = [] 47 | # 遍历后 n 行(假设输入严格满足 n 行字符串) 48 | for s in data[1:n+1]: 49 | if s not in seen: 50 | seen.add(s) 51 | output.append(s) 52 | # 一次性写入输出 53 | sys.stdout.write("\n".join(output)) 54 | 55 | if __name__ == '__main__': 56 | main() 57 | 58 | ``` 59 | ### Java 60 | ``` java 61 | import java.io.*; 62 | import java.util.*; 63 | 64 | public class Main { 65 | public static void main(String[] args) throws IOException { 66 | // 使用 BufferedReader 加速输入,StringBuilder 累积输出 67 | BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 68 | int n = Integer.parseInt(br.readLine()); 69 | Set seen = new HashSet<>(); 70 | StringBuilder sb = new StringBuilder(); 71 | 72 | for (int i = 0; i < n; i++) { 73 | String s = br.readLine(); 74 | if (seen.add(s)) { // add 返回 true 表示 s 是新出现的 75 | sb.append(s).append('\n'); 76 | } 77 | } 78 | System.out.print(sb); 79 | } 80 | } 81 | 82 | ``` -------------------------------------------------------------------------------- /problems/0217.小美的01矩阵.md: -------------------------------------------------------------------------------- 1 | # 模拟 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1297) 3 | ## 思路分析 4 | 因为数据范围比较小,两层循环分别暴力枚举即可。 5 | ## 复杂度分析 6 | 1. 时间复杂度:O(n*m); 7 | 2. 空间复杂度:O(1),不计输入输出。 8 | ## 代码实现 9 | ### Cpp 10 | ``` cpp 11 | #include 12 | using namespace std; 13 | const int N = 10005, MOD = 1000000007; 14 | using LL = long long; 15 | using PIL = pair; 16 | using PII = pair; 17 | int t, ans = 0; 18 | int main() { 19 | int n, m; 20 | cin >> n >> m; 21 | char **grid = new char*[n]; 22 | for (int i = 0; i < n; ++i) { 23 | grid[i] = new char[m]; 24 | scanf("%s", grid[i]); 25 | } 26 | for (int i = 0; i < n - 1; ++i) { 27 | for (int j = 0; j < m - 1; ++j) { 28 | int cnt = 0; 29 | if (grid[i][j] == '1') ++cnt; 30 | if (grid[i][j+1] == '1') ++cnt; 31 | if (grid[i+1][j] == '1') ++cnt; 32 | if (grid[i+1][j+1] == '1') ++cnt; 33 | if (cnt == 2) ++ans; 34 | } 35 | } 36 | cout << ans << '\n'; 37 | return 0; 38 | } 39 | ``` 40 | ### Python 41 | ``` python 42 | n, m = map(int, input().split()) 43 | grid = [input().strip() for _ in range(n)] 44 | 45 | ans = 0 46 | for i in range(n - 1): 47 | for j in range(m - 1): 48 | cnt = 0 49 | if grid[i][j] == '1': 50 | cnt += 1 51 | if grid[i][j + 1] == '1': 52 | cnt += 1 53 | if grid[i + 1][j] == '1': 54 | cnt += 1 55 | if grid[i + 1][j + 1] == '1': 56 | cnt += 1 57 | if cnt == 2: 58 | ans += 1 59 | 60 | print(ans) 61 | ``` 62 | ### Java 63 | ``` java 64 | import java.util.Scanner; 65 | 66 | public class Main { 67 | public static void main(String[] args) { 68 | Scanner sc = new Scanner(System.in); 69 | int n = sc.nextInt(); 70 | int m = sc.nextInt(); 71 | sc.nextLine(); // Consume the newline character 72 | 73 | char[][] grid = new char[n][m]; 74 | for (int i = 0; i < n; i++) { 75 | grid[i] = sc.nextLine().toCharArray(); 76 | } 77 | 78 | int ans = 0; 79 | for (int i = 0; i < n - 1; i++) { 80 | for (int j = 0; j < m - 1; j++) { 81 | int cnt = 0; 82 | if (grid[i][j] == '1') cnt++; 83 | if (grid[i][j + 1] == '1') cnt++; 84 | if (grid[i + 1][j] == '1') cnt++; 85 | if (grid[i + 1][j + 1] == '1') cnt++; 86 | if (cnt == 2) ans++; 87 | } 88 | } 89 | 90 | System.out.println(ans); 91 | } 92 | } 93 | ``` -------------------------------------------------------------------------------- /problems/0218.小美的回文子串.md: -------------------------------------------------------------------------------- 1 | # 脑筋急转弯 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1298) 3 | ## 思路分析 4 | 其实仔细分析题目就会发现,我们只需要把一段连续的相同的字符删掉,只留下一个即可。 5 | 6 | 因为最小的偶数长度的回文串(非空)为2。而长度为2的回文串是两个字符相同。要想不能有偶数长度的回文串,那就只能将所有连续相同字符全部删掉,只剩下一个。 7 | 8 | **为什么只要考虑长度为2的情况,不考虑其他的偶数**?因为不管是什么偶数长度的回文串,其回文中心一定是一个长度为2回文串。如"abccba", "tyuiooiuyt"都是这样的情况,所以我们只需要考虑破坏所有长度为2的回文串即可。 9 | ## 复杂度分析 10 | 1. 时间复杂度:O(n); 11 | 2. 空间复杂度:O(1),不计输入输出。 12 | ## 代码实现 13 | ### Cpp 14 | ``` cpp 15 | #include 16 | #define endl '\n' 17 | const int N = 100005, MOD = 1000000007; 18 | using namespace std; 19 | using LL = long long; 20 | using PIL = pair; 21 | using PII = pair; 22 | int t = 0, dir[]{1, 0, -1, 0, 1}; 23 | LL ans = 0; 24 | int main() { 25 | int n; 26 | cin >> n; 27 | char *str = new char[n+1]{}; 28 | cin >> str; 29 | for (int i = 1; i < n; ++i) { 30 | if (str[i] == str[i-1]) ++ans; 31 | } 32 | cout << ans << endl; 33 | return 0; 34 | } 35 | ``` 36 | ### Python 37 | ``` python 38 | n = int(input()) # 读取字符串长度 39 | s = input().strip() # 读取字符串并去除首尾空白字符 40 | 41 | ans = 0 42 | for i in range(1, n): 43 | if s[i] == s[i - 1]: 44 | ans += 1 45 | 46 | print(ans) 47 | ``` 48 | ### Java 49 | ``` java 50 | import java.util.Scanner; 51 | 52 | public class Main { 53 | public static void main(String[] args) { 54 | Scanner sc = new Scanner(System.in); 55 | int n = sc.nextInt(); // 读取字符串长度 56 | sc.nextLine(); // 消耗换行符 57 | String str = sc.nextLine(); // 读取字符串 58 | 59 | long ans = 0; 60 | for (int i = 1; i < n; i++) { 61 | if (str.charAt(i) == str.charAt(i - 1)) { 62 | ans++; 63 | } 64 | } 65 | 66 | System.out.println(ans); 67 | } 68 | } 69 | ``` -------------------------------------------------------------------------------- /problems/0222.计算出非回文子串长度.md: -------------------------------------------------------------------------------- 1 | # 模拟 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1302) 3 | ## 思路分析 4 | 根据题意模拟即可,外层循环枚举长度i,内层循环枚举以j为起始的长度为i的子串是否是回文串。找到一个非回文的子串就可以提前结束内层循环。 5 | ## 复杂度分析 6 | 1. 时间复杂度:O(n^3); 7 | 2. 空间复杂度:O(n)。 8 | ## 代码实现 9 | ### Cpp 10 | ``` cpp 11 | #include 12 | using namespace std; 13 | 14 | bool is_special(char c) { 15 | return c == '!' || c == '@' || c == '#' || c == '$' || c == '%' || c == '&' || c == '*' || c == '?'; 16 | } 17 | 18 | int main() { 19 | ios::sync_with_stdio(false); 20 | cin.tie(nullptr); 21 | string s, ns; 22 | getline(cin, s); 23 | transform(s.begin(), s.end(), s.begin(), ::tolower); 24 | // 过滤特殊字符 25 | for (char c : s) { 26 | if (!is_special(c)) ns += c; 27 | } 28 | int n = ns.size(); 29 | vector ans(s.size(), -1); // 结果数组,默认填充 -1 30 | // 遍历所有子串长度 31 | for (int i = 1; i <= n; ++i) { 32 | for (int j = 0; j <= n - i; ++j) { 33 | string sub = ns.substr(j, i); 34 | string rev_sub = sub; 35 | reverse(rev_sub.begin(), rev_sub.end()); 36 | if (sub != rev_sub) { 37 | ans[i - 1] = i; 38 | break; 39 | } 40 | } 41 | } 42 | // 格式化输出 43 | cout << "["; 44 | for (size_t i = 0; i < ans.size(); ++i) { 45 | if (i > 0) cout << ","; 46 | cout << ans[i]; 47 | } 48 | cout << "]\n"; 49 | return 0; 50 | } 51 | ``` 52 | ### Python 53 | ``` python 54 | s = input().lower() 55 | ns = '' 56 | for x in s: 57 | if x in "!@#$%&*?": 58 | continue 59 | ns += x 60 | n = len(ns) 61 | ans = [-1] * len(s) 62 | 63 | for i in range(1, n + 1): 64 | for j in range(n - i + 1): 65 | if ns[j:j + i] != ns[j:j + i][::-1]: 66 | ans[i-1] = i 67 | break 68 | ans = ','.join(map(str, ans)) 69 | print(f'[{ans}]') 70 | ``` 71 | ### Java 72 | ``` java 73 | import java.util.*; 74 | 75 | public class Main { 76 | public static boolean isSpecial(char c) { 77 | return "!@#$%&*?".indexOf(c) != -1; 78 | } 79 | 80 | public static void main(String[] args) { 81 | Scanner scanner = new Scanner(System.in); 82 | String s = scanner.nextLine().toLowerCase(); 83 | scanner.close(); 84 | StringBuilder ns = new StringBuilder(); 85 | for (char c : s.toCharArray()) { 86 | if (!isSpecial(c)) ns.append(c); 87 | } 88 | int n = ns.length(); 89 | int[] ans = new int[s.length()]; 90 | Arrays.fill(ans, -1); // 初始化为 -1 91 | // 遍历所有子串长度 92 | for (int i = 1; i <= n; i++) { 93 | for (int j = 0; j <= n - i; j++) { 94 | String sub = ns.substring(j, j + i); 95 | String revSub = new StringBuilder(sub).reverse().toString(); 96 | if (!sub.equals(revSub)) { 97 | ans[i - 1] = i; 98 | break; 99 | } 100 | } 101 | } 102 | // **手动格式化数组输出,确保没有空格** 103 | System.out.print("["); 104 | for (int i = 0; i < ans.length; i++) { 105 | if (i > 0) System.out.print(","); 106 | System.out.print(ans[i]); 107 | } 108 | System.out.println("]"); 109 | } 110 | } 111 | ``` -------------------------------------------------------------------------------- /problems/0227.藻类的总重量.md: -------------------------------------------------------------------------------- 1 | # 模拟 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1307) 3 | ## 思路分析 4 | 直接按照公式的描述输出即可。 5 | ## 复杂度分析 6 | 1. 时间复杂度:O(1); 7 | 2. 空间复杂度:O(1)。 8 | ## 代码实现 9 | ### Cpp 10 | ``` cpp 11 | #include 12 | #define endl '\n' 13 | using namespace std; 14 | int main() { 15 | int r, d, x; 16 | cin >> r >> d >> x; 17 | for (int i = 0; i < 10; ++i) { 18 | x = r * x - d; 19 | cout << x << endl; 20 | } 21 | return 0; 22 | } 23 | ``` 24 | ### Python 25 | ``` python 26 | r, d, x = map(int, input().split()) 27 | for _ in range(10): 28 | x = r * x - d 29 | print(x) 30 | ``` 31 | ### Java 32 | ``` java 33 | import java.util.Scanner; 34 | 35 | public class Main { 36 | public static void main(String[] args) { 37 | Scanner sc = new Scanner(System.in); // 创建 Scanner 对象用于输入 38 | int r = sc.nextInt(); // 读取 r 39 | int d = sc.nextInt(); // 读取 d 40 | int x = sc.nextInt(); // 读取 x 41 | 42 | for (int i = 0; i < 10; ++i) { 43 | x = r * x - d; // 计算新的 x 44 | System.out.println(x); // 输出结果 45 | } 46 | 47 | sc.close(); // 关闭 Scanner 48 | } 49 | } 50 | ``` -------------------------------------------------------------------------------- /problems/0228.吃豆人游戏.md: -------------------------------------------------------------------------------- 1 | # 模拟 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1308) 3 | ## 思路分析 4 | 从前往后遍历字符串,如果遇到了一次完整的"pacman"子串就可以将ans加一,否则就继续往后面遍历。 5 | 6 | 进阶做法:如果不是要寻找"pacman"子串的话,那么需要用到kmp算法进行字符串的匹配。这里由于模式串比较小,所以可以暴力查找。有兴趣的自己尝试一下用kmp算法实现。 7 | ## 复杂度分析 8 | 1. 时间复杂度:O(n); 9 | 2. 空间复杂度:O(1)。 10 | ## 代码实现 11 | ### Cpp 12 | ``` cpp 13 | #include 14 | using namespace std; 15 | void solve() { 16 | string s; 17 | cin >> s; 18 | //pacman 19 | int ans = 0; 20 | int n = s.size(); 21 | for(int i = 0;i <= n - 6;i++) { 22 | if (s.substr(i, 6) == "pacman") { 23 | ans++; 24 | } 25 | } 26 | 27 | cout << ans << "\n"; 28 | } 29 | int main() { 30 | solve(); 31 | } 32 | ``` 33 | ### Python 34 | ``` python 35 | s = input() 36 | ans, i = 0, 0 37 | n = len(s) - 5 38 | while i < n: 39 | if s[i:i+6] == "pacman": 40 | ans += 1 41 | i += 6 42 | else: 43 | i += 1 44 | print(ans) 45 | ``` 46 | ### Java 47 | ``` java 48 | import java.util.Scanner; 49 | 50 | public class Main { 51 | public static int minReplacements(String s) { 52 | String target = "pacman"; 53 | int n = s.length(); 54 | int count = 0; 55 | int i = 0; 56 | 57 | while (i <= n - 6) { 58 | // 检查当前子串是否是 "pacman" 59 | if (s.substring(i, i + 6).equals(target)) { 60 | count++; // 需要替换一次 61 | i += 6; // 跳过这6个字符 62 | } else { 63 | i++; // 继续检查下一个字符 64 | } 65 | } 66 | 67 | return count; 68 | } 69 | 70 | public static void main(String[] args) { 71 | String s; 72 | Scanner read = new Scanner(System.in); 73 | s = read.next(); 74 | System.out.println(minReplacements(s)); // 输出: 1 75 | } 76 | } 77 | ``` -------------------------------------------------------------------------------- /problems/0229.平衡子串的长度.md: -------------------------------------------------------------------------------- 1 | # 哈希表 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1309) 3 | ## 思路分析 4 | 题目要求原字符串中的一段子串,其满足'A'和'B'的数量相等。那么我们可以在遍历字符串的时候,用一个变量记录当前'A'和'B'的数量的绝对差值,只有当这个绝对差值是0的时候,'A'和'B'的数量才相等。也就是说,我们找到了一个满足题意的子串,这个时候我们直接求max即可得到最终的答案。那么要如何保证考虑到所有的情况呢? 5 | 6 | 可以用一个哈希表记录当前的'A'和'B'的数量的差值d,如果在此前哈希表中已经有这个d的时候,那么说明从这一段到出现这个d的位置'A'和'B'的数量是相等的。假设我们遍历到第i个位置,当前的d是5(这意味着'A'比'B'多5个,或者少5个)。**那么如果在哈希表中存在5这个键的话,假设其下标为j,那么从j -> i的这段区间,'A'和'B'的数量差值是为0的**。所以我们利用max函数更新ans即可;如果这个d不在哈希表中,那么去我们就将d加入哈希表,**并且哈希的键和值分别为[k,v] = [d,i],i表示当前遍历到的字符串的下标**。 7 | 8 | 要保证求得答案是最长的,需要保证插入到哈希表中的元素是第一次出现的下标才行,因为是第一次出现,所以下次出现在哈希表中找到的一定是最长的。 9 | ## 复杂度分析 10 | 1. 时间复杂度:O(n); 11 | 2. 空间复杂度:O(n)。 12 | ## 代码实现 13 | ### Cpp 14 | ``` cpp 15 | #include 16 | using namespace std; 17 | void solve() { 18 | string s; 19 | cin >> s; 20 | int n = s.size(); 21 | unordered_map ump; 22 | mp[0] = -1; 23 | int sum = 0, ans = 0; 24 | for(int i = 0; i < n; i++) { 25 | sum += (s[i] == 'A' ? 1 : -1); 26 | if(mp.count(sum)) { 27 | ans = max(ans, i - mp[sum]); 28 | } 29 | if(!mp.count(sum)) { 30 | mp[sum] = i; 31 | } 32 | } 33 | cout << ans << "\n"; 34 | } 35 | int main() { 36 | solve(); 37 | } 38 | ``` 39 | ### Python 40 | ``` python 41 | s = input().strip() 42 | ans = 0 43 | cur = 0 44 | sum_map = {0: -1} # 初始时前缀和为0的位置是-1 45 | for i in range(len(s)): 46 | if s[i] == 'A': 47 | cur += 1 48 | else: 49 | cur -= 1 50 | if cur in sum_map: 51 | ans = max(ans, i - sum_map[cur]) 52 | else: 53 | sum_map[cur] = i 54 | print(ans) 55 | ``` 56 | ### Java 57 | ``` java 58 | import java.util.HashMap; 59 | import java.util.Map; 60 | import java.util.Scanner; 61 | 62 | public class Main { 63 | public static void main(String[] args) { 64 | Scanner scanner = new Scanner(System.in); 65 | String s = scanner.nextLine().trim(); // 读取输入字符串 66 | int maxLen = 0; // 最大长度 67 | int currentSum = 0; // 当前前缀和 68 | Map sumMap = new HashMap<>(); // 哈希表记录前缀和及其索引 69 | sumMap.put(0, -1); // 初始前缀和为0的位置是-1 70 | for (int i = 0; i < s.length(); i++) { 71 | // 更新当前前缀和 72 | if (s.charAt(i) == 'A') { 73 | currentSum += 1; 74 | } else { 75 | currentSum -= 1; 76 | } 77 | // 检查当前前缀和是否在哈希表中 78 | if (sumMap.containsKey(currentSum)) { 79 | maxLen = Math.max(maxLen, i - sumMap.get(currentSum)); 80 | } else { 81 | sumMap.put(currentSum, i); // 记录当前前缀和及其索引 82 | } 83 | } 84 | System.out.println(maxLen); // 输出结果 85 | } 86 | } 87 | ``` -------------------------------------------------------------------------------- /problems/0241.小杰打怪物.md: -------------------------------------------------------------------------------- 1 | # 排序 + 贪心 2 | [题目链接](https://kamacoder.com/problempage.php?pid=1321) 3 | ## 思路分析 4 | 根据题意,不难发现,要想攻击次数最少,需要合理利用怪物的自爆才能达到最高的攻击效率。所以对数组进行排序,从血量最小的怪物开始,如果当前的怪物的血量小于等于当前已经自爆的怪物的累积伤害,那么我们就可以不需要进行攻击消灭这个怪物,并累积他的伤害。如果当前的怪物血量高于当前累积的伤害,那么就补上这个差额,再累积他自爆所能造成的伤害。 5 | ## 复杂度分析 6 | 1. 时间复杂度:O(nlog(n)); 7 | 2. 空间复杂度:O(1)。 8 | ## 代码实现 9 | ### Cpp 10 | ``` cpp 11 | #include 12 | #define endl '\n' 13 | const int N = 500, MOD = 1000000007; 14 | using namespace std; 15 | using LL = long long; 16 | using PIL = pair; 17 | using PII = pair; 18 | using T3I = tuple; 19 | int dir[]{1, 0, -1, 0, 1}; 20 | int main() { 21 | cin.tie(nullptr) -> sync_with_stdio(false); 22 | int n; 23 | cin >> n; 24 | int *nums = new int[n]; 25 | for (int i = 0; i < n; ++i) cin >> nums[i]; 26 | // 排序 27 | sort(nums, nums + n); 28 | LL ans = 0, cur = 0; 29 | for (int i = 0; i < n; ++i) { 30 | if (nums[i] > cur) { 31 | ans += nums[i] - cur; 32 | } 33 | cur += nums[i]; 34 | } 35 | cout << ans << endl; 36 | return 0; 37 | } 38 | ``` 39 | ### Python 40 | ``` python 41 | import sys 42 | 43 | def main(): 44 | n = int(sys.stdin.readline()) 45 | nums = list(map(int, sys.stdin.readline().split())) 46 | nums.sort() 47 | ans = 0 48 | cur = 0 49 | for num in nums: 50 | if num > cur: 51 | ans += num - cur 52 | cur += num 53 | print(ans) 54 | 55 | if __name__ == "__main__": 56 | main() 57 | ``` 58 | ### Java 59 | ``` java 60 | import java.io.*; 61 | import java.util.*; 62 | 63 | public class Main { 64 | public static void main(String[] args) throws IOException { 65 | BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 66 | StringTokenizer st = new StringTokenizer(br.readLine()); 67 | 68 | int n = Integer.parseInt(st.nextToken()); 69 | int[] nums = new int[n]; 70 | 71 | st = new StringTokenizer(br.readLine()); 72 | for (int i = 0; i < n; i++) { 73 | nums[i] = Integer.parseInt(st.nextToken()); 74 | } 75 | 76 | // 排序 77 | Arrays.sort(nums); 78 | 79 | long ans = 0; 80 | long cur = 0; 81 | for (int num : nums) { 82 | if (num > cur) { 83 | ans += num - cur; 84 | } 85 | cur += num; 86 | } 87 | 88 | System.out.println(ans); 89 | } 90 | } 91 | ``` 92 | ### Go 93 | ``` go 94 | package main 95 | 96 | import ( 97 | "bufio" 98 | "fmt" 99 | "os" 100 | "sort" 101 | ) 102 | 103 | func main() { 104 | scanner := bufio.NewScanner(os.Stdin) 105 | scanner.Split(bufio.ScanWords) 106 | 107 | // 读取 n 108 | scanner.Scan() 109 | n := 0 110 | fmt.Sscanf(scanner.Text(), "%d", &n) 111 | 112 | // 读取 nums 113 | nums := make([]int, n) 114 | for i := 0; i < n; i++ { 115 | scanner.Scan() 116 | fmt.Sscanf(scanner.Text(), "%d", &nums[i]) 117 | } 118 | 119 | // 排序 120 | sort.Ints(nums) 121 | 122 | ans := 0 123 | cur := 0 124 | for _, num := range nums { 125 | if num > cur { 126 | ans += num - cur 127 | } 128 | cur += num 129 | } 130 | 131 | fmt.Println(ans) 132 | } 133 | ``` --------------------------------------------------------------------------------