mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-07 07:35:35 +08:00
Merge branch 'master' into master
This commit is contained in:
@ -105,8 +105,11 @@
|
||||
3. [数组:27.移除元素](./problems/0027.移除元素.md)
|
||||
4. [数组:977.有序数组的平方](./problems/0977.有序数组的平方.md)
|
||||
5. [数组:209.长度最小的子数组](./problems/0209.长度最小的子数组.md)
|
||||
6. [数组:区间和](./problems/kamacoder/0058.区间和.md)
|
||||
6. [数组:59.螺旋矩阵II](./problems/0059.螺旋矩阵II.md)
|
||||
7. [数组:总结篇](./problems/数组总结篇.md)
|
||||
7. [数组:区间和](./problems/kamacoder/0058.区间和.md)
|
||||
8. [数组:开发商购买土地](./problems/kamacoder/0044.开发商购买土地.md)
|
||||
9. [数组:总结篇](./problems/数组总结篇.md)
|
||||
|
||||
## 链表
|
||||
|
||||
|
@ -262,6 +262,11 @@ class Solution {
|
||||
|
||||
for (int j = i + 1; j < nums.length; j++) {
|
||||
|
||||
// nums[i]+nums[j] > target 直接返回, 剪枝操作
|
||||
if (nums[i]+nums[j] > 0 && nums[i]+nums[j] > target) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (j > i + 1 && nums[j - 1] == nums[j]) { // 对nums[j]去重
|
||||
continue;
|
||||
}
|
||||
|
@ -42,7 +42,8 @@
|
||||
|
||||
我以[1,2,3]为例,抽象成树形结构如下:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### 回溯三部曲
|
||||
|
||||
@ -54,7 +55,7 @@
|
||||
|
||||
但排列问题需要一个used数组,标记已经选择的元素,如图橘黄色部分所示:
|
||||
|
||||

|
||||

|
||||
|
||||
代码如下:
|
||||
|
||||
@ -66,7 +67,7 @@ void backtracking (vector<int>& nums, vector<bool>& used)
|
||||
|
||||
* 递归终止条件
|
||||
|
||||

|
||||

|
||||
|
||||
可以看出叶子节点,就是收割结果的地方。
|
||||
|
||||
|
@ -371,6 +371,7 @@ class Solution:
|
||||
```
|
||||
### Go
|
||||
|
||||
动态规划
|
||||
```Go
|
||||
func uniquePaths(m int, n int) int {
|
||||
dp := make([][]int, m)
|
||||
@ -390,6 +391,26 @@ func uniquePaths(m int, n int) int {
|
||||
}
|
||||
```
|
||||
|
||||
数论方法
|
||||
```Go
|
||||
func uniquePaths(m int, n int) int {
|
||||
numerator := 1
|
||||
denominator := m - 1
|
||||
count := m - 1
|
||||
t := m + n - 2
|
||||
for count > 0 {
|
||||
numerator *= t
|
||||
t--
|
||||
for denominator != 0 && numerator % denominator == 0 {
|
||||
numerator /= denominator
|
||||
denominator--
|
||||
}
|
||||
count--
|
||||
}
|
||||
return numerator
|
||||
}
|
||||
```
|
||||
|
||||
### Javascript
|
||||
|
||||
```Javascript
|
||||
|
@ -287,6 +287,7 @@ function subsets(nums: number[]): number[][] {
|
||||
|
||||
### Rust
|
||||
|
||||
思路一:使用本题的标准解法,递归回溯。
|
||||
```Rust
|
||||
impl Solution {
|
||||
fn backtracking(result: &mut Vec<Vec<i32>>, path: &mut Vec<i32>, nums: &Vec<i32>, start_index: usize) {
|
||||
@ -308,6 +309,30 @@ impl Solution {
|
||||
}
|
||||
}
|
||||
```
|
||||
思路二:使用二进制枚举,n个元素的子集问题一共是$2^n$种情况。如果我们使用一个二进制数字,每一位根据0和1来决定是选取该元素与否,那么一共也是$2^n$的情况,正好可以一一对应,所以我们可以不使用递归,直接利用循环枚举完成子集问题。
|
||||
这种方法的优点在于效率高,不需要递归调用,并且代码容易编写。缺点则是过滤某些非法情况时会比递归方法难写一点,不过在子集问题中不存在这个问题。
|
||||
```Rust
|
||||
impl Solution {
|
||||
pub fn subsets(nums: Vec<i32>) -> Vec<Vec<i32>> {
|
||||
let n = nums.len();
|
||||
// 预分配2^n空间
|
||||
let mut result = Vec::with_capacity(1 << n);
|
||||
// 二进制枚举,2^n种情况
|
||||
for i in 0..(1 << n) {
|
||||
let mut subset = Vec::new();
|
||||
for j in 0..n {
|
||||
// 枚举该二进制数字的每一位
|
||||
// 如果该位是1,对应位置上的元素加入子集,否则跳过
|
||||
if i & (1 << j) != 0 {
|
||||
subset.push(nums[j]);
|
||||
}
|
||||
}
|
||||
result.push(subset);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### C
|
||||
|
||||
|
@ -794,6 +794,60 @@ func rebuild(inorder []int, postorder []int, rootIdx int, l, r int) *TreeNode {
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
/**
|
||||
* Definition for a binary tree node.
|
||||
* type TreeNode struct {
|
||||
* Val int
|
||||
* Left *TreeNode
|
||||
* Right *TreeNode
|
||||
* }
|
||||
*/
|
||||
func buildTree(inorder []int, postorder []int) *TreeNode {
|
||||
if len(postorder) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 后序遍历数组最后一个元素,就是当前的中间节点
|
||||
rootValue := postorder[len(postorder)-1]
|
||||
root := &TreeNode{Val:rootValue}
|
||||
|
||||
// 叶子结点
|
||||
if len(postorder) == 1 {
|
||||
return root
|
||||
}
|
||||
|
||||
// 找到中序遍历的切割点
|
||||
var delimiterIndex int
|
||||
for delimiterIndex = 0; delimiterIndex < len(inorder); delimiterIndex++ {
|
||||
if inorder[delimiterIndex] == rootValue {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 切割中序数组
|
||||
// 左闭右开区间:[0, delimiterIndex)
|
||||
leftInorder := inorder[:delimiterIndex]
|
||||
// [delimiterIndex + 1, end)
|
||||
rightInorder := inorder[delimiterIndex+1:]
|
||||
|
||||
// postorder 舍弃末尾元素
|
||||
postorder = postorder[:len(postorder)-1]
|
||||
|
||||
// 切割后序数组
|
||||
// 依然左闭右开,注意这里使用了左中序数组大小作为切割点
|
||||
// [0, len(leftInorder))
|
||||
leftPostorder := postorder[:len(leftInorder)]
|
||||
// [len(leftInorder), end)
|
||||
rightPostorder := postorder[len(leftInorder):]
|
||||
|
||||
root.Left = buildTree(leftInorder, leftPostorder)
|
||||
root.Right = buildTree(rightInorder, rightPostorder)
|
||||
|
||||
return root
|
||||
}
|
||||
```
|
||||
|
||||
105 从前序与中序遍历序列构造二叉树
|
||||
|
||||
```go
|
||||
@ -829,6 +883,60 @@ func build(pre []int, in []int, root int, l, r int) *TreeNode {
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
/**
|
||||
* Definition for a binary tree node.
|
||||
* type TreeNode struct {
|
||||
* Val int
|
||||
* Left *TreeNode
|
||||
* Right *TreeNode
|
||||
* }
|
||||
*/
|
||||
func buildTree(preorder []int, inorder []int) *TreeNode {
|
||||
if len(preorder) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 前序遍历数组第一个元素,就是当前的中间节点
|
||||
rootValue := preorder[0]
|
||||
root := &TreeNode{Val:rootValue}
|
||||
|
||||
// 叶子结点
|
||||
if len(preorder) == 1 {
|
||||
return root
|
||||
}
|
||||
|
||||
// 找到中序遍历的切割点
|
||||
var delimiterIndex int
|
||||
for delimiterIndex = 0; delimiterIndex < len(inorder); delimiterIndex++ {
|
||||
if inorder[delimiterIndex] == rootValue {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 切割中序数组
|
||||
// 左闭右开区间:[0, delimiterIndex)
|
||||
leftInorder := inorder[:delimiterIndex]
|
||||
// [delimiterIndex + 1, end)
|
||||
rightInorder := inorder[delimiterIndex+1:]
|
||||
|
||||
// preorder 舍弃首位元素
|
||||
preorder = preorder[1:]
|
||||
|
||||
// 切割前序数组
|
||||
// 依然左闭右开,注意这里使用了左中序数组大小作为切割点
|
||||
// [0, len(leftInorder))
|
||||
leftPreorder := preorder[:len(leftInorder)]
|
||||
// [len(leftInorder), end)
|
||||
rightPreorder := preorder[len(leftInorder):]
|
||||
|
||||
root.Left = buildTree(leftPreorder, leftInorder)
|
||||
root.Right = buildTree(rightPreorder, rightInorder)
|
||||
|
||||
return root
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### JavaScript
|
||||
|
||||
|
@ -623,6 +623,8 @@ class Solution:
|
||||
```
|
||||
### Go:
|
||||
|
||||
递归法
|
||||
|
||||
```Go
|
||||
func isBalanced(root *TreeNode) bool {
|
||||
h := getHeight(root)
|
||||
@ -653,6 +655,64 @@ func max(a, b int) int {
|
||||
}
|
||||
```
|
||||
|
||||
迭代法
|
||||
|
||||
```Go
|
||||
func isBalanced(root *TreeNode) bool {
|
||||
st := make([]*TreeNode, 0)
|
||||
if root == nil {
|
||||
return true
|
||||
}
|
||||
st = append(st, root)
|
||||
for len(st) > 0 {
|
||||
node := st[len(st)-1]
|
||||
st = st[:len(st)-1]
|
||||
if math.Abs(float64(getDepth(node.Left)) - float64(getDepth(node.Right))) > 1 {
|
||||
return false
|
||||
}
|
||||
if node.Right != nil {
|
||||
st = append(st, node.Right)
|
||||
}
|
||||
if node.Left != nil {
|
||||
st = append(st, node.Left)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getDepth(cur *TreeNode) int {
|
||||
st := make([]*TreeNode, 0)
|
||||
if cur != nil {
|
||||
st = append(st, cur)
|
||||
}
|
||||
depth := 0
|
||||
result := 0
|
||||
for len(st) > 0 {
|
||||
node := st[len(st)-1]
|
||||
if node != nil {
|
||||
st = st[:len(st)-1]
|
||||
st = append(st, node, nil)
|
||||
depth++
|
||||
if node.Right != nil {
|
||||
st = append(st, node.Right)
|
||||
}
|
||||
if node.Left != nil {
|
||||
st = append(st, node.Left)
|
||||
}
|
||||
} else {
|
||||
st = st[:len(st)-1]
|
||||
node = st[len(st)-1]
|
||||
st = st[:len(st)-1]
|
||||
depth--
|
||||
}
|
||||
if result < depth {
|
||||
result = depth
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
### JavaScript:
|
||||
|
||||
递归法:
|
||||
|
@ -308,7 +308,7 @@ class Solution:
|
||||
class Solution:
|
||||
def maxProfit(self, prices: List[int]) -> int:
|
||||
length = len(prices)
|
||||
if len == 0:
|
||||
if length == 0:
|
||||
return 0
|
||||
dp = [[0] * 2 for _ in range(length)]
|
||||
dp[0][0] = -prices[0]
|
||||
@ -466,7 +466,7 @@ function maxProfit(prices: number[]): number {
|
||||
};
|
||||
```
|
||||
|
||||
> 动态规划
|
||||
> 动态规划:版本一
|
||||
|
||||
```typescript
|
||||
function maxProfit(prices: number[]): number {
|
||||
@ -487,6 +487,26 @@ function maxProfit(prices: number[]): number {
|
||||
};
|
||||
```
|
||||
|
||||
> 动态规划:版本二
|
||||
|
||||
```typescript
|
||||
// dp[i][0] 表示第i天持有股票所得最多现金
|
||||
// dp[i][1] 表示第i天不持有股票所得最多现金
|
||||
function maxProfit(prices: number[]): number {
|
||||
const dp:number[][] = Array(2).fill(0).map(item => Array(2));
|
||||
dp[0][0] = -prices[0];
|
||||
dp[0][1] = 0;
|
||||
|
||||
for (let i = 1; i < prices.length; i++) {
|
||||
dp[i % 2][0] = Math.max(dp[(i - 1) % 2][0], -prices[i]);
|
||||
dp[i % 2][1] = Math.max(dp[(i - 1) % 2][1], dp[(i - 1) % 2][0] + prices[i]);
|
||||
}
|
||||
|
||||
// 返回不持有股票的最大现金
|
||||
return dp[(prices.length-1) % 2][1];
|
||||
};
|
||||
```
|
||||
|
||||
### C#:
|
||||
|
||||
> 贪心法
|
||||
|
@ -310,39 +310,35 @@ public:
|
||||
### Java
|
||||
```Java
|
||||
class Solution {
|
||||
List<List<String>> lists = new ArrayList<>();
|
||||
Deque<String> deque = new LinkedList<>();
|
||||
|
||||
//保持前几题一贯的格式, initialization
|
||||
List<List<String>> res = new ArrayList<>();
|
||||
List<String> cur = new ArrayList<>();
|
||||
public List<List<String>> partition(String s) {
|
||||
backTracking(s, 0);
|
||||
return lists;
|
||||
backtracking(s, 0, new StringBuilder());
|
||||
return res;
|
||||
}
|
||||
|
||||
private void backTracking(String s, int startIndex) {
|
||||
//如果起始位置大于s的大小,说明找到了一组分割方案
|
||||
if (startIndex >= s.length()) {
|
||||
lists.add(new ArrayList(deque));
|
||||
private void backtracking(String s, int start, StringBuilder sb){
|
||||
//因为是起始位置一个一个加的,所以结束时start一定等于s.length,因为进入backtracking时一定末尾也是回文,所以cur是满足条件的
|
||||
if (start == s.length()){
|
||||
//注意创建一个新的copy
|
||||
res.add(new ArrayList<>(cur));
|
||||
return;
|
||||
}
|
||||
for (int i = startIndex; i < s.length(); i++) {
|
||||
//如果是回文子串,则记录
|
||||
if (isPalindrome(s, startIndex, i)) {
|
||||
String str = s.substring(startIndex, i + 1);
|
||||
deque.addLast(str);
|
||||
} else {
|
||||
continue;
|
||||
//像前两题一样从前往后搜索,如果发现回文,进入backtracking,起始位置后移一位,循环结束照例移除cur的末位
|
||||
for (int i = start; i < s.length(); i++){
|
||||
sb.append(s.charAt(i));
|
||||
if (check(sb)){
|
||||
cur.add(sb.toString());
|
||||
backtracking(s, i + 1, new StringBuilder());
|
||||
cur.remove(cur.size() -1 );
|
||||
}
|
||||
//起始位置后移,保证不重复
|
||||
backTracking(s, i + 1);
|
||||
deque.removeLast();
|
||||
}
|
||||
}
|
||||
//判断是否是回文串
|
||||
private boolean isPalindrome(String s, int startIndex, int end) {
|
||||
for (int i = startIndex, j = end; i < j; i++, j--) {
|
||||
if (s.charAt(i) != s.charAt(j)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//helper method, 检查是否是回文
|
||||
private boolean check(StringBuilder sb){
|
||||
for (int i = 0; i < sb.length()/ 2; i++){
|
||||
if (sb.charAt(i) != sb.charAt(sb.length() - 1 - i)){return false;}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -249,6 +249,29 @@ class Solution {
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
// 解法3
|
||||
class Solution {
|
||||
public int canCompleteCircuit(int[] gas, int[] cost) {
|
||||
int tank = 0; // 当前油量
|
||||
int totalGas = 0; // 总加油量
|
||||
int totalCost = 0; // 总油耗
|
||||
int start = 0; // 起点
|
||||
for (int i = 0; i < gas.length; i++) {
|
||||
totalGas += gas[i];
|
||||
totalCost += cost[i];
|
||||
|
||||
tank += gas[i] - cost[i];
|
||||
if (tank < 0) { // tank 变为负数 意味着 从0到i之间出发都不能顺利环路一周,因为在此i点必会没油
|
||||
tank = 0; // reset tank,类似于题目53.最大子树和reset sum
|
||||
start = i + 1; // 起点变为i点往后一位
|
||||
}
|
||||
}
|
||||
if (totalCost > totalGas) return -1;
|
||||
return start;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Python
|
||||
暴力法
|
||||
@ -322,6 +345,37 @@ class Solution:
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
贪心算法(方法一)
|
||||
```go
|
||||
func canCompleteCircuit(gas []int, cost []int) int {
|
||||
curSum := 0
|
||||
min := math.MaxInt64
|
||||
for i := 0; i < len(gas); i++ {
|
||||
rest := gas[i] - cost[i]
|
||||
curSum += rest
|
||||
if curSum < min {
|
||||
min = curSum
|
||||
}
|
||||
}
|
||||
if curSum < 0 {
|
||||
return -1
|
||||
}
|
||||
if min >= 0 {
|
||||
return 0
|
||||
}
|
||||
for i := len(gas) - 1; i > 0; i-- {
|
||||
rest := gas[i] - cost[i]
|
||||
min += rest
|
||||
if min >= 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
```
|
||||
|
||||
贪心算法(方法二)
|
||||
```go
|
||||
func canCompleteCircuit(gas []int, cost []int) int {
|
||||
curSum := 0
|
||||
|
@ -474,6 +474,7 @@ class Solution:
|
||||
words = s.split() #type(words) --- list
|
||||
words = words[::-1] # 反转单词
|
||||
return ' '.join(words) #列表转换成字符串
|
||||
```
|
||||
|
||||
### Go:
|
||||
|
||||
|
@ -41,7 +41,7 @@
|
||||
首先通过本题大家要明确什么是子序列,“子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序”。
|
||||
|
||||
本题也是代码随想录中子序列问题的第一题,如果没接触过这种题目的话,本题还是很难的,甚至想暴力去搜索也不知道怎么搜。
|
||||
子序列问题是动态规划解决的经典问题,当前下标i的递增子序列长度,其实和i之前的下表j的子序列长度有关系,那又是什么样的关系呢。
|
||||
子序列问题是动态规划解决的经典问题,当前下标i的递增子序列长度,其实和i之前的下标j的子序列长度有关系,那又是什么样的关系呢。
|
||||
|
||||
接下来,我们依然用动规五部曲来详细分析一波:
|
||||
|
||||
|
@ -359,6 +359,26 @@ func max(a, b int) int {
|
||||
|
||||
### Javascript:
|
||||
|
||||
> 不同的状态定义 感觉更容易理解些
|
||||
```javascript
|
||||
function maxProfit(prices) {
|
||||
// 第i天状态 持股 卖出 非冷冻期(不持股) 处于冷冻期
|
||||
const dp = new Array(prices.length).fill(0).map(() => [0, 0, 0, 0]);
|
||||
dp[0][0] = -prices[0];
|
||||
for (let i = 1; i < prices.length; i++) {
|
||||
// 持股
|
||||
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i]);
|
||||
// 卖出
|
||||
dp[i][1] = dp[i - 1][0] + prices[i];
|
||||
// 非冷冻期(不持股)
|
||||
dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1]);
|
||||
// 冷冻期(上一天卖出)
|
||||
dp[i][3] = dp[i - 1][1];
|
||||
}
|
||||
return Math.max(...dp.pop());
|
||||
};
|
||||
```
|
||||
|
||||
```javascript
|
||||
const maxProfit = (prices) => {
|
||||
if(prices.length < 2) {
|
||||
|
@ -511,7 +511,7 @@ object Solution {
|
||||
|
||||
```
|
||||
|
||||
###Ruby
|
||||
### Ruby:
|
||||
```ruby
|
||||
def intersection(nums1, nums2)
|
||||
hash = {}
|
||||
|
@ -337,6 +337,21 @@ func sumOfLeftLeaves(root *TreeNode) int {
|
||||
}
|
||||
```
|
||||
|
||||
**递归精简版**
|
||||
|
||||
```go
|
||||
func sumOfLeftLeaves(root *TreeNode) int {
|
||||
if root == nil {
|
||||
return 0
|
||||
}
|
||||
leftValue := 0
|
||||
if root.Left != nil && root.Left.Left == nil && root.Left.Right == nil {
|
||||
leftValue = root.Left.Val
|
||||
}
|
||||
return leftValue + sumOfLeftLeaves(root.Left) + sumOfLeftLeaves(root.Right)
|
||||
}
|
||||
```
|
||||
|
||||
**迭代法(前序遍历)**
|
||||
|
||||
```go
|
||||
|
@ -87,7 +87,7 @@
|
||||
回归本题,整个插入过程如下:
|
||||
|
||||
排序完的people:
|
||||
[[7,0], [7,1], [6,1], [5,0], [5,2],[4,4]]
|
||||
[[7,0], [7,1], [6,1], [5,0], [5,2], [4,4]]
|
||||
|
||||
插入的过程:
|
||||
* 插入[7,0]:[[7,0]]
|
||||
|
@ -226,21 +226,36 @@ class Solution:
|
||||
```
|
||||
|
||||
### Go
|
||||
```golang
|
||||
//排序后,局部最优
|
||||
|
||||
版本一 大饼干优先
|
||||
```Go
|
||||
func findContentChildren(g []int, s []int) int {
|
||||
sort.Ints(g)
|
||||
sort.Ints(s)
|
||||
|
||||
// 从小到大
|
||||
child := 0
|
||||
for sIdx := 0; child < len(g) && sIdx < len(s); sIdx++ {
|
||||
if s[sIdx] >= g[child] {//如果饼干的大小大于或等于孩子的为空则给与,否则不给予,继续寻找选一个饼干是否符合
|
||||
child++
|
||||
sort.Ints(g)
|
||||
sort.Ints(s)
|
||||
index := len(s) - 1
|
||||
result := 0
|
||||
for i := len(g) - 1; i >= 0; i-- {
|
||||
if index >= 0 && s[index] >= g[i] {
|
||||
result++
|
||||
index--
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
return child
|
||||
版本二 小饼干优先
|
||||
```Go
|
||||
func findContentChildren(g []int, s []int) int {
|
||||
sort.Ints(g)
|
||||
sort.Ints(s)
|
||||
index := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
if index < len(g) && g[index] <= s[i] {
|
||||
index++
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -615,7 +615,7 @@ impl Solution {
|
||||
}
|
||||
}
|
||||
```
|
||||
## C
|
||||
### C
|
||||
|
||||
```c
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
@ -134,17 +134,17 @@ x = (target + sum) / 2
|
||||
|
||||
大家看到(target + sum) / 2 应该担心计算的过程中向下取整有没有影响。
|
||||
|
||||
这么担心就对了,例如sum 是5,S是2的话其实就是无解的,所以:
|
||||
这么担心就对了,例如sum是5,target是2 的话其实就是无解的,所以:
|
||||
|
||||
```CPP
|
||||
(C++代码中,输入的S 就是题目描述的 target)
|
||||
if ((S + sum) % 2 == 1) return 0; // 此时没有方案
|
||||
if ((target + sum) % 2 == 1) return 0; // 此时没有方案
|
||||
```
|
||||
|
||||
同时如果 S的绝对值已经大于sum,那么也是没有方案的。
|
||||
同时如果target 的绝对值已经大于sum,那么也是没有方案的。
|
||||
|
||||
```CPP
|
||||
(C++代码中,输入的S 就是题目描述的 target)
|
||||
if (abs(S) > sum) return 0; // 此时没有方案
|
||||
if (abs(target) > sum) return 0; // 此时没有方案
|
||||
```
|
||||
|
||||
再回归到01背包问题,为什么是01背包呢?
|
||||
@ -213,9 +213,9 @@ dp[j]其他下标对应的数值也应该初始化为0,从递推公式也可
|
||||
|
||||
5. 举例推导dp数组
|
||||
|
||||
输入:nums: [1, 1, 1, 1, 1], S: 3
|
||||
输入:nums: [1, 1, 1, 1, 1], target: 3
|
||||
|
||||
bagSize = (S + sum) / 2 = (3 + 5) / 2 = 4
|
||||
bagSize = (target + sum) / 2 = (3 + 5) / 2 = 4
|
||||
|
||||
dp数组状态变化如下:
|
||||
|
||||
@ -226,12 +226,12 @@ C++代码如下:
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int findTargetSumWays(vector<int>& nums, int S) {
|
||||
int findTargetSumWays(vector<int>& nums, int target) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < nums.size(); i++) sum += nums[i];
|
||||
if (abs(S) > sum) return 0; // 此时没有方案
|
||||
if ((S + sum) % 2 == 1) return 0; // 此时没有方案
|
||||
int bagSize = (S + sum) / 2;
|
||||
if (abs(target) > sum) return 0; // 此时没有方案
|
||||
if ((target + sum) % 2 == 1) return 0; // 此时没有方案
|
||||
int bagSize = (target + sum) / 2;
|
||||
vector<int> dp(bagSize + 1, 0);
|
||||
dp[0] = 1;
|
||||
for (int i = 0; i < nums.size(); i++) {
|
||||
@ -585,7 +585,7 @@ impl Solution {
|
||||
}
|
||||
}
|
||||
```
|
||||
## C
|
||||
### C
|
||||
|
||||
```c
|
||||
int getSum(int * nums, int numsSize){
|
||||
@ -655,3 +655,52 @@ public class Solution
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
|
||||
</a>
|
||||
|
||||
|
||||
class Solution {
|
||||
public:
|
||||
int findTargetSumWays(vector<int>& nums, int target) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < nums.size(); i++) sum += nums[i];
|
||||
if (abs(target) > sum) return 0; // 此时没有方案
|
||||
if ((target + sum) % 2 == 1) return 0; // 此时没有方案
|
||||
int bagSize = (target + sum) / 2;
|
||||
|
||||
vector<vector<int>> dp(nums.size(), vector<int>(bagSize + 1, 0));
|
||||
|
||||
if (nums[0] <= bagSize) dp[0][nums[0]] = 1;
|
||||
|
||||
dp[0][0] = 1;
|
||||
|
||||
int numZero = 0;
|
||||
for (int i = 0; i < nums.size(); i++) {
|
||||
if (nums[i] == 0) numZero++;
|
||||
dp[i][0] = (int) pow(2.0, numZero);
|
||||
}
|
||||
|
||||
for (int i = 1; i < nums.size(); i++) {
|
||||
for (int j = 0; j <= bagSize; j++) {
|
||||
if (nums[i] > j) dp[i][j] = dp[i - 1][j];
|
||||
else dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]];
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < nums.size(); i++) {
|
||||
for (int j = 0; j <= bagSize; j++) {
|
||||
cout << dp[i][j] << " ";
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
return dp[nums.size() - 1][bagSize];
|
||||
}
|
||||
};
|
||||
|
||||
1 1 0 0 0
|
||||
1 2 1 0 0
|
||||
1 3 3 1 0
|
||||
1 4 6 4 1
|
||||
1 5 10 10 5
|
||||
|
||||
初始化 如果没有0, dp[i][0] = 1; 即所有元素都不取。
|
||||
|
||||
用元素 取与不取来举例
|
||||
|
||||
|
@ -55,7 +55,7 @@ dp[i] 和 dp[i-1] ,dp[i + 1] 看上去都没啥关系。
|
||||
我们在判断字符串S是否是回文,那么如果我们知道 s[1],s[2],s[3] 这个子串是回文的,那么只需要比较 s[0]和s[4]这两个元素是否相同,如果相同的话,这个字符串s 就是回文串。
|
||||
|
||||
|
||||
那么此时我们是不是能找到一种递归关系,也就是判断一个子字符串(字符串的下表范围[i,j])是否回文,依赖于,子字符串(下表范围[i + 1, j - 1])) 是否是回文。
|
||||
那么此时我们是不是能找到一种递归关系,也就是判断一个子字符串(字符串下标范围[i,j])是否回文,依赖于,子字符串(下标范围[i + 1, j - 1])) 是否是回文。
|
||||
|
||||
所以为了明确这种递归关系,我们的dp数组是要定义成一位二维dp数组。
|
||||
|
||||
|
@ -165,112 +165,240 @@ private:
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
### C++双链表法:
|
||||
|
||||
```CPP
|
||||
//采用循环虚拟结点的双链表实现
|
||||
class MyLinkedList {
|
||||
public:
|
||||
// 定义双向链表节点结构体
|
||||
struct DList {
|
||||
int elem; // 节点存储的元素
|
||||
DList *next; // 指向下一个节点的指针
|
||||
DList *prev; // 指向上一个节点的指针
|
||||
// 构造函数,创建一个值为elem的新节点
|
||||
DList(int elem) : elem(elem), next(nullptr), prev(nullptr) {};
|
||||
};
|
||||
|
||||
// 构造函数,初始化链表
|
||||
MyLinkedList() {
|
||||
sentinelNode = new DList(0); // 创建哨兵节点,不存储有效数据
|
||||
sentinelNode->next = sentinelNode; // 哨兵节点的下一个节点指向自身,形成循环
|
||||
sentinelNode->prev = sentinelNode; // 哨兵节点的上一个节点指向自身,形成循环
|
||||
size = 0; // 初始化链表大小为0
|
||||
}
|
||||
|
||||
// 获取链表中第index个节点的值
|
||||
int get(int index) {
|
||||
if (index > (size - 1) || index < 0) { // 检查索引是否超出范围
|
||||
return -1; // 如果超出范围,返回-1
|
||||
}
|
||||
int num;
|
||||
int mid = size >> 1; // 计算链表中部位置
|
||||
DList *curNode = sentinelNode; // 从哨兵节点开始
|
||||
if (index < mid) { // 如果索引小于中部位置,从前往后遍历
|
||||
for (int i = 0; i < index + 1; i++) {
|
||||
curNode = curNode->next; // 移动到目标节点
|
||||
}
|
||||
} else { // 如果索引大于等于中部位置,从后往前遍历
|
||||
for (int i = 0; i < size - index; i++) {
|
||||
curNode = curNode->prev; // 移动到目标节点
|
||||
}
|
||||
}
|
||||
num = curNode->elem; // 获取目标节点的值
|
||||
return num; // 返回节点的值
|
||||
}
|
||||
|
||||
// 在链表头部添加节点
|
||||
void addAtHead(int val) {
|
||||
DList *newNode = new DList(val); // 创建新节点
|
||||
DList *next = sentinelNode->next; // 获取当前头节点的下一个节点
|
||||
newNode->prev = sentinelNode; // 新节点的上一个节点指向哨兵节点
|
||||
newNode->next = next; // 新节点的下一个节点指向原来的头节点
|
||||
size++; // 链表大小加1
|
||||
sentinelNode->next = newNode; // 哨兵节点的下一个节点指向新节点
|
||||
next->prev = newNode; // 原来的头节点的上一个节点指向新节点
|
||||
}
|
||||
|
||||
// 在链表尾部添加节点
|
||||
void addAtTail(int val) {
|
||||
DList *newNode = new DList(val); // 创建新节点
|
||||
DList *prev = sentinelNode->prev; // 获取当前尾节点的上一个节点
|
||||
newNode->next = sentinelNode; // 新节点的下一个节点指向哨兵节点
|
||||
newNode->prev = prev; // 新节点的上一个节点指向原来的尾节点
|
||||
size++; // 链表大小加1
|
||||
sentinelNode->prev = newNode; // 哨兵节点的上一个节点指向新节点
|
||||
prev->next = newNode; // 原来的尾节点的下一个节点指向新节点
|
||||
}
|
||||
|
||||
// 在链表中的第index个节点之前添加值为val的节点
|
||||
void addAtIndex(int index, int val) {
|
||||
if (index > size) { // 检查索引是否超出范围
|
||||
return; // 如果超出范围,直接返回
|
||||
}
|
||||
if (index <= 0) { // 如果索引为0或负数,在头部添加节点
|
||||
addAtHead(val);
|
||||
return;
|
||||
}
|
||||
int num;
|
||||
int mid = size >> 1; // 计算链表中部位置
|
||||
DList *curNode = sentinelNode; // 从哨兵节点开始
|
||||
if (index < mid) { // 如果索引小于中部位置,从前往后遍历
|
||||
for (int i = 0; i < index; i++) {
|
||||
curNode = curNode->next; // 移动到目标位置的前一个节点
|
||||
}
|
||||
DList *temp = curNode->next; // 获取目标位置的节点
|
||||
DList *newNode = new DList(val); // 创建新节点
|
||||
curNode->next = newNode; // 在目标位置前添加新节点
|
||||
temp->prev = newNode; // 目标位置的节点的前一个节点指向新节点
|
||||
newNode->next = temp; // 新节点的下一个节点指向目标位置的结点
|
||||
newNode->prev = curNode; // 新节点的上一个节点指向当前节点
|
||||
} else { // 如果索引大于等于中部位置,从后往前遍历
|
||||
for (int i = 0; i < size - index; i++) {
|
||||
curNode = curNode->prev; // 移动到目标位置的后一个节点
|
||||
}
|
||||
DList *temp = curNode->prev; // 获取目标位置的节点
|
||||
DList *newNode = new DList(val); // 创建新节点
|
||||
curNode->prev = newNode; // 在目标位置后添加新节点
|
||||
temp->next = newNode; // 目标位置的节点的下一个节点指向新节点
|
||||
newNode->prev = temp; // 新节点的上一个节点指向目标位置的节点
|
||||
newNode->next = curNode; // 新节点的下一个节点指向当前节点
|
||||
}
|
||||
size++; // 链表大小加1
|
||||
}
|
||||
|
||||
// 删除链表中的第index个节点
|
||||
void deleteAtIndex(int index) {
|
||||
if (index > (size - 1) || index < 0) { // 检查索引是否超出范围
|
||||
return; // 如果超出范围,直接返回
|
||||
}
|
||||
int num;
|
||||
int mid = size >> 1; // 计算链表中部位置
|
||||
DList *curNode = sentinelNode; // 从哨兵节点开始
|
||||
if (index < mid) { // 如果索引小于中部位置,从前往后遍历
|
||||
for (int i = 0; i < index; i++) {
|
||||
curNode = curNode->next; // 移动到目标位置的前一个节点
|
||||
}
|
||||
DList *next = curNode->next->next; // 获取目标位置的下一个节点
|
||||
curNode->next = next; // 删除目标位置的节点
|
||||
next->prev = curNode; // 目标位置的下一个节点的前一个节点指向当前节点
|
||||
} else { // 如果索引大于等于中部位置,从后往前遍历
|
||||
for (int i = 0; i < size - index - 1; i++) {
|
||||
curNode = curNode->prev; // 移动到目标位置的后一个节点
|
||||
}
|
||||
DList *prev = curNode->prev->prev; // 获取目标位置的下一个节点
|
||||
curNode->prev = prev; // 删除目标位置的节点
|
||||
prev->next = curNode; // 目标位置的下一个节点的下一个节点指向当前节点
|
||||
}
|
||||
size--; // 链表大小减1
|
||||
}
|
||||
|
||||
private:
|
||||
int size; // 链表的大小
|
||||
DList *sentinelNode; // 哨兵节点的指针
|
||||
};
|
||||
```
|
||||
|
||||
### C:
|
||||
|
||||
```C
|
||||
typedef struct MyLinkedList {
|
||||
int val;
|
||||
struct MyLinkedList* next;
|
||||
}MyLinkedList;
|
||||
typedef struct Node {
|
||||
int val;
|
||||
struct Node* next;
|
||||
} Node;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int size;
|
||||
Node* data;
|
||||
} MyLinkedList;
|
||||
|
||||
/** Initialize your data structure here. */
|
||||
|
||||
MyLinkedList* myLinkedListCreate() {
|
||||
//这个题必须用虚拟头指针,参数都是一级指针,头节点确定后没法改指向了!!!
|
||||
MyLinkedList* head = (MyLinkedList *)malloc(sizeof (MyLinkedList));
|
||||
head->next = NULL;
|
||||
return head;
|
||||
MyLinkedList* obj = (MyLinkedList*)malloc(sizeof(MyLinkedList));
|
||||
Node* head = (Node*)malloc(sizeof(Node));
|
||||
head->next = (void*)0;
|
||||
obj->data = head;
|
||||
obj->size = 0;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
|
||||
int myLinkedListGet(MyLinkedList* obj, int index) {
|
||||
MyLinkedList *cur = obj->next;
|
||||
for (int i = 0; cur != NULL; i++){
|
||||
if (i == index){
|
||||
return cur->val;
|
||||
}
|
||||
else{
|
||||
cur = cur->next;
|
||||
}
|
||||
if (index < 0 || index >= obj->size) return -1;
|
||||
|
||||
Node* cur = obj->data;
|
||||
while (index-- >= 0) {
|
||||
cur = cur->next;
|
||||
}
|
||||
return -1;
|
||||
|
||||
return cur->val;
|
||||
}
|
||||
|
||||
/** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
|
||||
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
|
||||
MyLinkedList *nhead = (MyLinkedList *)malloc(sizeof (MyLinkedList));
|
||||
nhead->val = val;
|
||||
nhead->next = obj->next;
|
||||
obj->next = nhead;
|
||||
Node* node = (Node*)malloc(sizeof(Node));
|
||||
node->val = val;
|
||||
|
||||
node->next = obj->data->next;
|
||||
obj->data->next = node;
|
||||
obj->size++;
|
||||
}
|
||||
|
||||
/** Append a node of value val to the last element of the linked list. */
|
||||
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
|
||||
MyLinkedList *cur = obj;
|
||||
while(cur->next != NULL){
|
||||
Node* cur = obj->data;
|
||||
while (cur->next != ((void*)0)) {
|
||||
cur = cur->next;
|
||||
}
|
||||
MyLinkedList *ntail = (MyLinkedList *)malloc(sizeof (MyLinkedList));
|
||||
ntail->val = val;
|
||||
ntail->next = NULL;
|
||||
cur->next = ntail;
|
||||
|
||||
Node* tail = (Node*)malloc(sizeof(Node));
|
||||
tail->val = val;
|
||||
tail->next = (void*)0;
|
||||
cur->next = tail;
|
||||
obj->size++;
|
||||
}
|
||||
|
||||
/** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
|
||||
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
|
||||
if (index == 0){
|
||||
myLinkedListAddAtHead(obj, val);
|
||||
return;
|
||||
}
|
||||
MyLinkedList *cur = obj->next;
|
||||
for (int i = 1 ;cur != NULL; i++){
|
||||
if (i == index){
|
||||
MyLinkedList* newnode = (MyLinkedList *)malloc(sizeof (MyLinkedList));
|
||||
newnode->val = val;
|
||||
newnode->next = cur->next;
|
||||
cur->next = newnode;
|
||||
return;
|
||||
}
|
||||
else{
|
||||
cur = cur->next;
|
||||
}
|
||||
if (index > obj->size) return;
|
||||
|
||||
Node* cur = obj->data;
|
||||
while (index-- > 0) {
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
Node* node = (Node*)malloc(sizeof(Node));
|
||||
node->val = val;
|
||||
node->next = cur->next;
|
||||
cur->next = node;
|
||||
obj->size++;
|
||||
}
|
||||
|
||||
/** Delete the index-th node in the linked list, if the index is valid. */
|
||||
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
|
||||
if (index == 0){
|
||||
MyLinkedList *tmp = obj->next;
|
||||
if (tmp != NULL){
|
||||
obj->next = tmp->next;
|
||||
free(tmp);
|
||||
}
|
||||
return;
|
||||
if (index < 0 || index >= obj->size) return;
|
||||
|
||||
Node* cur = obj->data;
|
||||
while (index-- > 0) {
|
||||
cur = cur->next;
|
||||
}
|
||||
MyLinkedList *cur = obj->next;
|
||||
for (int i = 1 ;cur != NULL && cur->next != NULL; i++){
|
||||
if (i == index){
|
||||
MyLinkedList *tmp = cur->next;
|
||||
if (tmp != NULL) {
|
||||
cur->next = tmp->next;
|
||||
free(tmp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else{
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Node* temp = cur->next;
|
||||
cur->next = temp->next;
|
||||
free(temp);
|
||||
obj->size--;
|
||||
}
|
||||
|
||||
void myLinkedListFree(MyLinkedList* obj) {
|
||||
while(obj != NULL){
|
||||
MyLinkedList *tmp = obj;
|
||||
obj = obj->next;
|
||||
free(tmp);
|
||||
}
|
||||
Node* tmp = obj->data;
|
||||
while (tmp != NULL) {
|
||||
Node* n = tmp;
|
||||
tmp = tmp->next;
|
||||
free(n);
|
||||
}
|
||||
free(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1569,3 +1697,4 @@ public class MyLinkedList
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
|
||||
</a>
|
||||
|
||||
|
@ -100,6 +100,18 @@ public:
|
||||
## 其他语言版本
|
||||
|
||||
### Java:
|
||||
排序法
|
||||
```Java
|
||||
class Solution {
|
||||
public int[] sortedSquares(int[] nums) {
|
||||
for (int i = 0; i < nums.length; i++) {
|
||||
nums[i] = nums[i] * nums[i];
|
||||
}
|
||||
Arrays.sort(nums);
|
||||
return nums;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```Java
|
||||
class Solution {
|
||||
|
@ -128,6 +128,36 @@ class Solution {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 版本二:排序数组并贪心地尽可能将负数翻转为正数,再根据剩余的k值调整最小元素的符号,从而最大化数组的总和。
|
||||
class Solution {
|
||||
public int largestSumAfterKNegations(int[] nums, int k) {
|
||||
if (nums.length == 1) return nums[0];
|
||||
|
||||
// 排序:先把负数处理了
|
||||
Arrays.sort(nums);
|
||||
|
||||
for (int i = 0; i < nums.length && k > 0; i++) { // 贪心点, 通过负转正, 消耗尽可能多的k
|
||||
if (nums[i] < 0) {
|
||||
nums[i] = -nums[i];
|
||||
k--;
|
||||
}
|
||||
}
|
||||
|
||||
// 退出循环, k > 0 || k < 0 (k消耗完了不用讨论)
|
||||
if (k % 2 == 1) { // k > 0 && k is odd:对于负数:负-正-负-正
|
||||
Arrays.sort(nums); // 再次排序得到剩余的负数,或者最小的正数
|
||||
nums[0] = -nums[0];
|
||||
}
|
||||
// k > 0 && k is even,flip数字不会产生影响: 对于负数: 负-正-负;对于正数:正-负-正
|
||||
|
||||
int sum = 0;
|
||||
for (int num : nums) { // 计算最大和
|
||||
sum += num;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Python
|
||||
|
390
problems/kamacoder/0044.开发商购买土地.md
Normal file
390
problems/kamacoder/0044.开发商购买土地.md
Normal file
@ -0,0 +1,390 @@
|
||||
|
||||
# 44. 开发商购买土地
|
||||
|
||||
> 本题为代码随想录后续扩充题目,还没有视频讲解,顺便让大家练习一下ACM输入输出模式(笔试面试必备)
|
||||
|
||||
[题目链接](https://kamacoder.com/problempage.php?pid=1044)
|
||||
|
||||
【题目描述】
|
||||
|
||||
在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。
|
||||
|
||||
现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。
|
||||
|
||||
然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。
|
||||
|
||||
为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。
|
||||
|
||||
注意:区块不可再分。
|
||||
|
||||
【输入描述】
|
||||
|
||||
第一行输入两个正整数,代表 n 和 m。
|
||||
|
||||
接下来的 n 行,每行输出 m 个正整数。
|
||||
|
||||
输出描述
|
||||
|
||||
请输出一个整数,代表两个子区域内土地总价值之间的最小差距。
|
||||
|
||||
【输入示例】
|
||||
|
||||
3 3
|
||||
1 2 3
|
||||
2 1 3
|
||||
1 2 3
|
||||
|
||||
【输出示例】
|
||||
|
||||
0
|
||||
|
||||
【提示信息】
|
||||
|
||||
如果将区域按照如下方式划分:
|
||||
|
||||
1 2 | 3
|
||||
2 1 | 3
|
||||
1 2 | 3
|
||||
|
||||
两个子区域内土地总价值之间的最小差距可以达到 0。
|
||||
|
||||
【数据范围】:
|
||||
|
||||
* 1 <= n, m <= 100;
|
||||
* n 和 m 不同时为 1。
|
||||
|
||||
## 思路
|
||||
|
||||
看到本题,大家如果想暴力求解,应该是 n^3 的时间复杂度,
|
||||
|
||||
一个 for 枚举分割线, 嵌套 两个for 去累加区间里的和。
|
||||
|
||||
如果本题要求 任何两个行(或者列)之间的数值总和,大家在[0058.区间和](./0058.区间和.md) 的基础上 应该知道怎么求。
|
||||
|
||||
就是前缀和的思路,先统计好,前n行的和 q[n],如果要求矩阵 a行 到 b行 之间的总和,那么就 q[b] - q[a - 1]就好。
|
||||
|
||||
至于为什么是 a - 1,大家去看 [0058.区间和](./0058.区间和.md) 的分析,使用 前缀和 要注意 区间左右边的开闭情况。
|
||||
|
||||
本题也可以使用 前缀和的思路来求解,先将 行方向,和 列方向的和求出来,这样可以方便知道 划分的两个区间的和。
|
||||
|
||||
代码如下:
|
||||
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <climits>
|
||||
|
||||
using namespace std;
|
||||
int main () {
|
||||
int n, m;
|
||||
cin >> n >> m;
|
||||
int sum = 0;
|
||||
vector<vector<int>> vec(n, vector<int>(m, 0)) ;
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < m; j++) {
|
||||
cin >> vec[i][j];
|
||||
sum += vec[i][j];
|
||||
}
|
||||
}
|
||||
// 统计横向
|
||||
vector<int> horizontal(n, 0);
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0 ; j < m; j++) {
|
||||
horizontal[i] += vec[i][j];
|
||||
}
|
||||
}
|
||||
// 统计纵向
|
||||
vector<int> vertical(m , 0);
|
||||
for (int j = 0; j < m; j++) {
|
||||
for (int i = 0 ; i < n; i++) {
|
||||
vertical[j] += vec[i][j];
|
||||
}
|
||||
}
|
||||
int result = INT_MAX;
|
||||
int horizontalCut = 0;
|
||||
for (int i = 0 ; i < n; i++) {
|
||||
horizontalCut += horizontal[i];
|
||||
result = min(result, abs(sum - horizontalCut - horizontalCut));
|
||||
}
|
||||
int verticalCut = 0;
|
||||
for (int j = 0; j < m; j++) {
|
||||
verticalCut += vertical[j];
|
||||
result = min(result, abs(sum - verticalCut - verticalCut));
|
||||
}
|
||||
cout << result << endl;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
时间复杂度: O(n^2)
|
||||
|
||||
其实本题可以在暴力求解的基础上,优化一下,就不用前缀和了,在行向遍历的时候,遇到行末尾就统一一下, 在列向遍历的时候,遇到列末尾就统计一下。
|
||||
|
||||
时间复杂度也是 O(n^2)
|
||||
|
||||
代码如下:
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <climits>
|
||||
|
||||
using namespace std;
|
||||
int main () {
|
||||
int n, m;
|
||||
cin >> n >> m;
|
||||
int sum = 0;
|
||||
vector<vector<int>> vec(n, vector<int>(m, 0)) ;
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < m; j++) {
|
||||
cin >> vec[i][j];
|
||||
sum += vec[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
int result = INT_MAX;
|
||||
int count = 0; // 统计遍历过的行
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0 ; j < m; j++) {
|
||||
count += vec[i][j];
|
||||
// 遍历到行末尾时候开始统计
|
||||
if (j == m - 1) result = min (result, abs(sum - count - count));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
count = 0; // 统计遍历过的列
|
||||
for (int j = 0; j < m; j++) {
|
||||
for (int i = 0 ; i < n; i++) {
|
||||
count += vec[i][j];
|
||||
// 遍历到列末尾的时候开始统计
|
||||
if (i == n - 1) result = min (result, abs(sum - count - count));
|
||||
}
|
||||
}
|
||||
cout << result << endl;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
### Java
|
||||
|
||||
前缀和
|
||||
|
||||
```Java
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int n = scanner.nextInt();
|
||||
int m = scanner.nextInt();
|
||||
int sum = 0;
|
||||
int[][] vec = new int[n][m];
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < m; j++) {
|
||||
vec[i][j] = scanner.nextInt();
|
||||
sum += vec[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// 统计横向
|
||||
int[] horizontal = new int[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < m; j++) {
|
||||
horizontal[i] += vec[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// 统计纵向
|
||||
int[] vertical = new int[m];
|
||||
for (int j = 0; j < m; j++) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
vertical[j] += vec[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
int result = Integer.MAX_VALUE;
|
||||
int horizontalCut = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
horizontalCut += horizontal[i];
|
||||
result = Math.min(result, Math.abs(sum - 2 * horizontalCut));
|
||||
}
|
||||
|
||||
int verticalCut = 0;
|
||||
for (int j = 0; j < m; j++) {
|
||||
verticalCut += vertical[j];
|
||||
result = Math.min(result, Math.abs(sum - 2 * verticalCut));
|
||||
}
|
||||
|
||||
System.out.println(result);
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
优化暴力
|
||||
|
||||
```Java
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int n = scanner.nextInt();
|
||||
int m = scanner.nextInt();
|
||||
int sum = 0;
|
||||
int[][] vec = new int[n][m];
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < m; j++) {
|
||||
vec[i][j] = scanner.nextInt();
|
||||
sum += vec[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
int result = Integer.MAX_VALUE;
|
||||
int count = 0; // 统计遍历过的行
|
||||
|
||||
// 行切分
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < m; j++) {
|
||||
count += vec[i][j];
|
||||
// 遍历到行末尾时候开始统计
|
||||
if (j == m - 1) {
|
||||
result = Math.min(result, Math.abs(sum - 2 * count));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
count = 0;
|
||||
// 列切分
|
||||
for (int j = 0; j < m; j++) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
count += vec[i][j];
|
||||
// 遍历到列末尾时候开始统计
|
||||
if (i == n - 1) {
|
||||
result = Math.min(result, Math.abs(sum - 2 * count));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(result);
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### python
|
||||
|
||||
前缀和
|
||||
```python
|
||||
def main():
|
||||
import sys
|
||||
input = sys.stdin.read
|
||||
data = input().split()
|
||||
|
||||
idx = 0
|
||||
n = int(data[idx])
|
||||
idx += 1
|
||||
m = int(data[idx])
|
||||
idx += 1
|
||||
sum = 0
|
||||
vec = []
|
||||
for i in range(n):
|
||||
row = []
|
||||
for j in range(m):
|
||||
num = int(data[idx])
|
||||
idx += 1
|
||||
row.append(num)
|
||||
sum += num
|
||||
vec.append(row)
|
||||
|
||||
# 统计横向
|
||||
horizontal = [0] * n
|
||||
for i in range(n):
|
||||
for j in range(m):
|
||||
horizontal[i] += vec[i][j]
|
||||
|
||||
# 统计纵向
|
||||
vertical = [0] * m
|
||||
for j in range(m):
|
||||
for i in range(n):
|
||||
vertical[j] += vec[i][j]
|
||||
|
||||
result = float('inf')
|
||||
horizontalCut = 0
|
||||
for i in range(n):
|
||||
horizontalCut += horizontal[i]
|
||||
result = min(result, abs(sum - 2 * horizontalCut))
|
||||
|
||||
verticalCut = 0
|
||||
for j in range(m):
|
||||
verticalCut += vertical[j]
|
||||
result = min(result, abs(sum - 2 * verticalCut))
|
||||
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
```
|
||||
|
||||
优化暴力
|
||||
|
||||
```python
|
||||
def main():
|
||||
import sys
|
||||
input = sys.stdin.read
|
||||
data = input().split()
|
||||
|
||||
idx = 0
|
||||
n = int(data[idx])
|
||||
idx += 1
|
||||
m = int(data[idx])
|
||||
idx += 1
|
||||
sum = 0
|
||||
vec = []
|
||||
for i in range(n):
|
||||
row = []
|
||||
for j in range(m):
|
||||
num = int(data[idx])
|
||||
idx += 1
|
||||
row.append(num)
|
||||
sum += num
|
||||
vec.append(row)
|
||||
|
||||
result = float('inf')
|
||||
|
||||
count = 0
|
||||
# 行切分
|
||||
for i in range(n):
|
||||
|
||||
for j in range(m):
|
||||
count += vec[i][j]
|
||||
# 遍历到行末尾时候开始统计
|
||||
if j == m - 1:
|
||||
result = min(result, abs(sum - 2 * count))
|
||||
|
||||
count = 0
|
||||
# 列切分
|
||||
for j in range(m):
|
||||
|
||||
for i in range(n):
|
||||
count += vec[i][j]
|
||||
# 遍历到列末尾时候开始统计
|
||||
if i == n - 1:
|
||||
result = min(result, abs(sum - 2 * count))
|
||||
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
```
|
@ -755,8 +755,160 @@ public class Main {
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
import heapq
|
||||
|
||||
class Edge:
|
||||
def __init__(self, to, val):
|
||||
self.to = to
|
||||
self.val = val
|
||||
|
||||
def dijkstra(n, m, edges, start, end):
|
||||
grid = [[] for _ in range(n + 1)]
|
||||
|
||||
for p1, p2, val in edges:
|
||||
grid[p1].append(Edge(p2, val))
|
||||
|
||||
minDist = [float('inf')] * (n + 1)
|
||||
visited = [False] * (n + 1)
|
||||
|
||||
pq = []
|
||||
heapq.heappush(pq, (0, start))
|
||||
minDist[start] = 0
|
||||
|
||||
while pq:
|
||||
cur_dist, cur_node = heapq.heappop(pq)
|
||||
|
||||
if visited[cur_node]:
|
||||
continue
|
||||
|
||||
visited[cur_node] = True
|
||||
|
||||
for edge in grid[cur_node]:
|
||||
if not visited[edge.to] and cur_dist + edge.val < minDist[edge.to]:
|
||||
minDist[edge.to] = cur_dist + edge.val
|
||||
heapq.heappush(pq, (minDist[edge.to], edge.to))
|
||||
|
||||
return -1 if minDist[end] == float('inf') else minDist[end]
|
||||
|
||||
# 输入
|
||||
n, m = map(int, input().split())
|
||||
edges = [tuple(map(int, input().split())) for _ in range(m)]
|
||||
start = 1 # 起点
|
||||
end = n # 终点
|
||||
|
||||
# 运行算法并输出结果
|
||||
result = dijkstra(n, m, edges, start, end)
|
||||
print(result)
|
||||
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Edge 表示带权重的边
|
||||
type Edge struct {
|
||||
to, val int
|
||||
}
|
||||
|
||||
// PriorityQueue 实现一个小顶堆
|
||||
type Item struct {
|
||||
node, dist int
|
||||
}
|
||||
|
||||
type PriorityQueue []*Item
|
||||
|
||||
func (pq PriorityQueue) Len() int { return len(pq) }
|
||||
|
||||
func (pq PriorityQueue) Less(i, j int) bool {
|
||||
return pq[i].dist < pq[j].dist
|
||||
}
|
||||
|
||||
func (pq PriorityQueue) Swap(i, j int) {
|
||||
pq[i], pq[j] = pq[j], pq[i]
|
||||
}
|
||||
|
||||
func (pq *PriorityQueue) Push(x interface{}) {
|
||||
*pq = append(*pq, x.(*Item))
|
||||
}
|
||||
|
||||
func (pq *PriorityQueue) Pop() interface{} {
|
||||
old := *pq
|
||||
n := len(old)
|
||||
item := old[n-1]
|
||||
*pq = old[0 : n-1]
|
||||
return item
|
||||
}
|
||||
|
||||
func dijkstra(n, m int, edges [][]int, start, end int) int {
|
||||
grid := make([][]Edge, n+1)
|
||||
for _, edge := range edges {
|
||||
p1, p2, val := edge[0], edge[1], edge[2]
|
||||
grid[p1] = append(grid[p1], Edge{to: p2, val: val})
|
||||
}
|
||||
|
||||
minDist := make([]int, n+1)
|
||||
for i := range minDist {
|
||||
minDist[i] = math.MaxInt64
|
||||
}
|
||||
visited := make([]bool, n+1)
|
||||
|
||||
pq := &PriorityQueue{}
|
||||
heap.Init(pq)
|
||||
heap.Push(pq, &Item{node: start, dist: 0})
|
||||
minDist[start] = 0
|
||||
|
||||
for pq.Len() > 0 {
|
||||
cur := heap.Pop(pq).(*Item)
|
||||
|
||||
if visited[cur.node] {
|
||||
continue
|
||||
}
|
||||
|
||||
visited[cur.node] = true
|
||||
|
||||
for _, edge := range grid[cur.node] {
|
||||
if !visited[edge.to] && minDist[cur.node]+edge.val < minDist[edge.to] {
|
||||
minDist[edge.to] = minDist[cur.node] + edge.val
|
||||
heap.Push(pq, &Item{node: edge.to, dist: minDist[edge.to]})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if minDist[end] == math.MaxInt64 {
|
||||
return -1
|
||||
}
|
||||
return minDist[end]
|
||||
}
|
||||
|
||||
func main() {
|
||||
var n, m int
|
||||
fmt.Scan(&n, &m)
|
||||
|
||||
edges := make([][]int, m)
|
||||
for i := 0; i < m; i++ {
|
||||
var p1, p2, val int
|
||||
fmt.Scan(&p1, &p2, &val)
|
||||
edges[i] = []int{p1, p2, val}
|
||||
}
|
||||
|
||||
start := 1 // 起点
|
||||
end := n // 终点
|
||||
|
||||
result := dijkstra(n, m, edges, start, end)
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Rust
|
||||
|
||||
### Javascript
|
||||
|
@ -806,6 +806,63 @@ public class Main {
|
||||
|
||||
### Python
|
||||
|
||||
```
|
||||
import sys
|
||||
|
||||
def dijkstra(n, m, edges, start, end):
|
||||
# 初始化邻接矩阵
|
||||
grid = [[float('inf')] * (n + 1) for _ in range(n + 1)]
|
||||
for p1, p2, val in edges:
|
||||
grid[p1][p2] = val
|
||||
|
||||
# 初始化距离数组和访问数组
|
||||
minDist = [float('inf')] * (n + 1)
|
||||
visited = [False] * (n + 1)
|
||||
|
||||
minDist[start] = 0 # 起始点到自身的距离为0
|
||||
|
||||
for _ in range(1, n + 1): # 遍历所有节点
|
||||
minVal = float('inf')
|
||||
cur = -1
|
||||
|
||||
# 选择距离源点最近且未访问过的节点
|
||||
for v in range(1, n + 1):
|
||||
if not visited[v] and minDist[v] < minVal:
|
||||
minVal = minDist[v]
|
||||
cur = v
|
||||
|
||||
if cur == -1: # 如果找不到未访问过的节点,提前结束
|
||||
break
|
||||
|
||||
visited[cur] = True # 标记该节点已被访问
|
||||
|
||||
# 更新未访问节点到源点的距离
|
||||
for v in range(1, n + 1):
|
||||
if not visited[v] and grid[cur][v] != float('inf') and minDist[cur] + grid[cur][v] < minDist[v]:
|
||||
minDist[v] = minDist[cur] + grid[cur][v]
|
||||
|
||||
return -1 if minDist[end] == float('inf') else minDist[end]
|
||||
|
||||
if __name__ == "__main__":
|
||||
input = sys.stdin.read
|
||||
data = input().split()
|
||||
n, m = int(data[0]), int(data[1])
|
||||
edges = []
|
||||
index = 2
|
||||
for _ in range(m):
|
||||
p1 = int(data[index])
|
||||
p2 = int(data[index + 1])
|
||||
val = int(data[index + 2])
|
||||
edges.append((p1, p2, val))
|
||||
index += 3
|
||||
start = 1 # 起点
|
||||
end = n # 终点
|
||||
|
||||
result = dijkstra(n, m, edges, start, end)
|
||||
print(result)
|
||||
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
### Rust
|
||||
|
@ -38,7 +38,7 @@
|
||||
5 6 2
|
||||
5 7 1
|
||||
6 7 1
|
||||
```
|
||||
```
|
||||
|
||||
输出示例:
|
||||
|
||||
@ -79,7 +79,7 @@ kruscal的思路:
|
||||
|
||||

|
||||
|
||||
--------
|
||||
--------
|
||||
|
||||
选边(4,5),节点4 和 节点 5 不在同一个集合,生成树可以添加边(4,5) ,并将节点4,节点5 放到同一个集合。
|
||||
|
||||
@ -87,7 +87,7 @@ kruscal的思路:
|
||||
|
||||
**大家判断两个节点是否在同一个集合,就看图中两个节点是否有绿色的粗线连着就行**
|
||||
|
||||
------
|
||||
------
|
||||
|
||||
(这里在强调一下,以下选边是按照上面排序好的边的数组来选择的)
|
||||
|
||||
@ -95,13 +95,13 @@ kruscal的思路:
|
||||
|
||||

|
||||
|
||||
---------
|
||||
---------
|
||||
|
||||
选边(2,6),节点2 和 节点6 不在同一个集合,生成树添加边(2,6),并将节点2,节点6 放到同一个集合。
|
||||
|
||||

|
||||
|
||||
--------
|
||||
--------
|
||||
|
||||
选边(3,4),节点3 和 节点4 不在同一个集合,生成树添加边(3,4),并将节点3,节点4 放到同一个集合。
|
||||
|
||||
@ -113,7 +113,7 @@ kruscal的思路:
|
||||
|
||||

|
||||
|
||||
-----------
|
||||
-----------
|
||||
|
||||
选边(5,7),节点5 和 节点7 在同一个集合,不做计算。
|
||||
|
||||
@ -122,7 +122,7 @@ kruscal的思路:
|
||||
后面遍历 边(3,2),(2,4),(5,6) 同理,都因两个节点已经在同一集合,不做计算。
|
||||
|
||||
|
||||
-------
|
||||
-------
|
||||
|
||||
此时 我们就已经生成了一个最小生成树,即:
|
||||
|
||||
@ -230,7 +230,7 @@ int main() {
|
||||
|
||||
如果题目要求将最小生成树的边输出的话,应该怎么办呢?
|
||||
|
||||
Kruskal 算法 输出边的话,相对prim 要容易很多,因为 Kruskal 本来就是直接操作边,边的结构自然清晰,不用像 prim一样 需要再节点练成线输出边 (因为prim是对节点操作,而 Kruskal是对边操作,这是本质区别)
|
||||
Kruskal 算法 输出边的话,相对prim 要容易很多,因为 Kruskal 本来就是直接操作边,边的结构自然清晰,不用像 prim一样 需要再将节点连成线输出边 (因为prim是对节点操作,而 Kruskal是对边操作,这是本质区别)
|
||||
|
||||
本题中,边的结构为:
|
||||
|
||||
@ -404,8 +404,145 @@ Kruskal算法 时间复杂度 为 nlogn,其中n 为边的数量,适用稀疏
|
||||
|
||||
### Java
|
||||
|
||||
```Java
|
||||
import java.util.*;
|
||||
|
||||
class Edge {
|
||||
int l, r, val;
|
||||
|
||||
Edge(int l, int r, int val) {
|
||||
this.l = l;
|
||||
this.r = r;
|
||||
this.val = val;
|
||||
}
|
||||
}
|
||||
|
||||
public class Main {
|
||||
private static int n = 10001;
|
||||
private static int[] father = new int[n];
|
||||
|
||||
// 并查集初始化
|
||||
public static void init() {
|
||||
for (int i = 0; i < n; i++) {
|
||||
father[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
// 并查集的查找操作
|
||||
public static int find(int u) {
|
||||
if (u == father[u]) return u;
|
||||
return father[u] = find(father[u]);
|
||||
}
|
||||
|
||||
// 并查集的加入集合
|
||||
public static void join(int u, int v) {
|
||||
u = find(u);
|
||||
v = find(v);
|
||||
if (u == v) return;
|
||||
father[v] = u;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int v = scanner.nextInt();
|
||||
int e = scanner.nextInt();
|
||||
List<Edge> edges = new ArrayList<>();
|
||||
int result_val = 0;
|
||||
|
||||
for (int i = 0; i < e; i++) {
|
||||
int v1 = scanner.nextInt();
|
||||
int v2 = scanner.nextInt();
|
||||
int val = scanner.nextInt();
|
||||
edges.add(new Edge(v1, v2, val));
|
||||
}
|
||||
|
||||
// 执行Kruskal算法
|
||||
edges.sort(Comparator.comparingInt(edge -> edge.val));
|
||||
|
||||
// 并查集初始化
|
||||
init();
|
||||
|
||||
// 从头开始遍历边
|
||||
for (Edge edge : edges) {
|
||||
int x = find(edge.l);
|
||||
int y = find(edge.r);
|
||||
|
||||
if (x != y) {
|
||||
result_val += edge.val;
|
||||
join(x, y);
|
||||
}
|
||||
}
|
||||
System.out.println(result_val);
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
class Edge:
|
||||
def __init__(self, l, r, val):
|
||||
self.l = l
|
||||
self.r = r
|
||||
self.val = val
|
||||
|
||||
n = 10001
|
||||
father = list(range(n))
|
||||
|
||||
def init():
|
||||
global father
|
||||
father = list(range(n))
|
||||
|
||||
def find(u):
|
||||
if u != father[u]:
|
||||
father[u] = find(father[u])
|
||||
return father[u]
|
||||
|
||||
def join(u, v):
|
||||
u = find(u)
|
||||
v = find(v)
|
||||
if u != v:
|
||||
father[v] = u
|
||||
|
||||
def kruskal(v, edges):
|
||||
edges.sort(key=lambda edge: edge.val)
|
||||
init()
|
||||
result_val = 0
|
||||
|
||||
for edge in edges:
|
||||
x = find(edge.l)
|
||||
y = find(edge.r)
|
||||
if x != y:
|
||||
result_val += edge.val
|
||||
join(x, y)
|
||||
|
||||
return result_val
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
input = sys.stdin.read
|
||||
data = input().split()
|
||||
|
||||
v = int(data[0])
|
||||
e = int(data[1])
|
||||
|
||||
edges = []
|
||||
index = 2
|
||||
for _ in range(e):
|
||||
v1 = int(data[index])
|
||||
v2 = int(data[index + 1])
|
||||
val = int(data[index + 2])
|
||||
edges.append(Edge(v1, v2, val))
|
||||
index += 3
|
||||
|
||||
result_val = kruskal(v, edges)
|
||||
print(result_val)
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Go
|
||||
|
||||
### Rust
|
||||
|
@ -520,6 +520,73 @@ int main() {
|
||||
|
||||
### Java
|
||||
|
||||
```Java
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int v = scanner.nextInt();
|
||||
int e = scanner.nextInt();
|
||||
|
||||
// 初始化邻接矩阵,所有值初始化为一个大值,表示无穷大
|
||||
int[][] grid = new int[v + 1][v + 1];
|
||||
for (int i = 0; i <= v; i++) {
|
||||
Arrays.fill(grid[i], 10001);
|
||||
}
|
||||
|
||||
// 读取边的信息并填充邻接矩阵
|
||||
for (int i = 0; i < e; i++) {
|
||||
int x = scanner.nextInt();
|
||||
int y = scanner.nextInt();
|
||||
int k = scanner.nextInt();
|
||||
grid[x][y] = k;
|
||||
grid[y][x] = k;
|
||||
}
|
||||
|
||||
// 所有节点到最小生成树的最小距离
|
||||
int[] minDist = new int[v + 1];
|
||||
Arrays.fill(minDist, 10001);
|
||||
|
||||
// 记录节点是否在树里
|
||||
boolean[] isInTree = new boolean[v + 1];
|
||||
|
||||
// Prim算法主循环
|
||||
for (int i = 1; i < v; i++) {
|
||||
int cur = -1;
|
||||
int minVal = Integer.MAX_VALUE;
|
||||
|
||||
// 选择距离生成树最近的节点
|
||||
for (int j = 1; j <= v; j++) {
|
||||
if (!isInTree[j] && minDist[j] < minVal) {
|
||||
minVal = minDist[j];
|
||||
cur = j;
|
||||
}
|
||||
}
|
||||
|
||||
// 将最近的节点加入生成树
|
||||
isInTree[cur] = true;
|
||||
|
||||
// 更新非生成树节点到生成树的距离
|
||||
for (int j = 1; j <= v; j++) {
|
||||
if (!isInTree[j] && grid[cur][j] < minDist[j]) {
|
||||
minDist[j] = grid[cur][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 统计结果
|
||||
int result = 0;
|
||||
for (int i = 2; i <= v; i++) {
|
||||
result += minDist[i];
|
||||
}
|
||||
System.out.println(result);
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Python
|
||||
```python
|
||||
# 接收输入
|
||||
@ -556,6 +623,71 @@ for i in range(2, v + 1):
|
||||
print(ans)
|
||||
```
|
||||
|
||||
```python
|
||||
def prim(v, e, edges):
|
||||
import sys
|
||||
import heapq
|
||||
|
||||
# 初始化邻接矩阵,所有值初始化为一个大值,表示无穷大
|
||||
grid = [[10001] * (v + 1) for _ in range(v + 1)]
|
||||
|
||||
# 读取边的信息并填充邻接矩阵
|
||||
for edge in edges:
|
||||
x, y, k = edge
|
||||
grid[x][y] = k
|
||||
grid[y][x] = k
|
||||
|
||||
# 所有节点到最小生成树的最小距离
|
||||
minDist = [10001] * (v + 1)
|
||||
|
||||
# 记录节点是否在树里
|
||||
isInTree = [False] * (v + 1)
|
||||
|
||||
# Prim算法主循环
|
||||
for i in range(1, v):
|
||||
cur = -1
|
||||
minVal = sys.maxsize
|
||||
|
||||
# 选择距离生成树最近的节点
|
||||
for j in range(1, v + 1):
|
||||
if not isInTree[j] and minDist[j] < minVal:
|
||||
minVal = minDist[j]
|
||||
cur = j
|
||||
|
||||
# 将最近的节点加入生成树
|
||||
isInTree[cur] = True
|
||||
|
||||
# 更新非生成树节点到生成树的距离
|
||||
for j in range(1, v + 1):
|
||||
if not isInTree[j] and grid[cur][j] < minDist[j]:
|
||||
minDist[j] = grid[cur][j]
|
||||
|
||||
# 统计结果
|
||||
result = sum(minDist[2:v+1])
|
||||
return result
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
input = sys.stdin.read
|
||||
data = input().split()
|
||||
|
||||
v = int(data[0])
|
||||
e = int(data[1])
|
||||
|
||||
edges = []
|
||||
index = 2
|
||||
for _ in range(e):
|
||||
x = int(data[index])
|
||||
y = int(data[index + 1])
|
||||
k = int(data[index + 2])
|
||||
edges.append((x, y, k))
|
||||
index += 3
|
||||
|
||||
result = prim(v, e, edges)
|
||||
print(result)
|
||||
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
### Rust
|
||||
|
@ -1,6 +1,8 @@
|
||||
|
||||
# 58. 区间和
|
||||
|
||||
> 本题为代码随想录后续扩充题目,还没有视频讲解,顺便让大家练习一下ACM输入输出模式(笔试面试必备)
|
||||
|
||||
[题目链接](https://kamacoder.com/problempage.php?pid=1070)
|
||||
|
||||
题目描述
|
||||
@ -93,16 +95,15 @@ int main() {
|
||||
|
||||

|
||||
|
||||
|
||||
如果,我们想统计,在vec数组上 下标 2 到下标 5 之间的累加和,那是不是就用 p[5] - p[1] 就可以了。
|
||||
|
||||
为什么呢?
|
||||
|
||||
p[1] = vec[0] + vec[1];
|
||||
`p[1] = vec[0] + vec[1];`
|
||||
|
||||
p[5] = vec[0] + vec[1] + vec[2] + vec[3] + vec[4] + vec[5];
|
||||
`p[5] = vec[0] + vec[1] + vec[2] + vec[3] + vec[4] + vec[5];`
|
||||
|
||||
p[5] - p[1] = vec[2] + vec[3] + vec[4] + vec[5];
|
||||
`p[5] - p[1] = vec[2] + vec[3] + vec[4] + vec[5];`
|
||||
|
||||
这不就是我们要求的 下标 2 到下标 5 之间的累加和吗。
|
||||
|
||||
@ -110,11 +111,17 @@ p[5] - p[1] = vec[2] + vec[3] + vec[4] + vec[5];
|
||||
|
||||

|
||||
|
||||
p[5] - p[1] 就是 红色部分的区间和。
|
||||
`p[5] - p[1]` 就是 红色部分的区间和。
|
||||
|
||||
而 p 数组是我们之前就计算好的累加和,所以后面每次求区间和的之后 我们只需要 O(1)的操作。
|
||||
而 p 数组是我们之前就计算好的累加和,所以后面每次求区间和的之后 我们只需要 O(1) 的操作。
|
||||
|
||||
**特别注意**: 在使用前缀和求解的时候,要特别注意 求解区间。
|
||||
|
||||
如上图,如果我们要求 区间下标 [2, 5] 的区间和,那么应该是 p[5] - p[1],而不是 p[5] - p[2]。
|
||||
|
||||
**很多录友在使用前缀和的时候,分不清前缀和的区间,建议画一画图,模拟一下 思路会更清晰**。
|
||||
|
||||
本题C++代码如下:
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
@ -142,6 +149,8 @@ int main() {
|
||||
|
||||
```
|
||||
|
||||
C++ 代码 面对大量数据 读取 输出操作,最好用scanf 和 printf,耗时会小很多:
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
@ -168,24 +177,89 @@ int main() {
|
||||
|
||||
```
|
||||
|
||||
```CPP
|
||||
## 其他语言版本
|
||||
|
||||
#include<bits/stdc++.h>
|
||||
using namespace std;
|
||||
### Java
|
||||
|
||||
```Java
|
||||
|
||||
int main(){
|
||||
int n, a, b;
|
||||
cin >> n;
|
||||
vector<int> vec(n + 1);
|
||||
vector<int> p(n + 1, 0);
|
||||
for(int i = 1; i <= n; i++) {
|
||||
scanf("%d", &vec[i]);
|
||||
p[i] = p[i - 1] + vec[i];
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
|
||||
int n = scanner.nextInt();
|
||||
int[] vec = new int[n];
|
||||
int[] p = new int[n];
|
||||
|
||||
int presum = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
vec[i] = scanner.nextInt();
|
||||
presum += vec[i];
|
||||
p[i] = presum;
|
||||
}
|
||||
|
||||
while (scanner.hasNextInt()) {
|
||||
int a = scanner.nextInt();
|
||||
int b = scanner.nextInt();
|
||||
|
||||
int sum;
|
||||
if (a == 0) {
|
||||
sum = p[b];
|
||||
} else {
|
||||
sum = p[b] - p[a - 1];
|
||||
}
|
||||
System.out.println(sum);
|
||||
}
|
||||
|
||||
scanner.close();
|
||||
}
|
||||
while(~scanf("%d%d", &a, &b)){
|
||||
printf("%d\n", p[b + 1] - p[a]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
|
||||
import sys
|
||||
input = sys.stdin.read
|
||||
|
||||
def main():
|
||||
data = input().split()
|
||||
index = 0
|
||||
n = int(data[index])
|
||||
index += 1
|
||||
vec = []
|
||||
for i in range(n):
|
||||
vec.append(int(data[index + i]))
|
||||
index += n
|
||||
|
||||
p = [0] * n
|
||||
presum = 0
|
||||
for i in range(n):
|
||||
presum += vec[i]
|
||||
p[i] = presum
|
||||
|
||||
results = []
|
||||
while index < len(data):
|
||||
a = int(data[index])
|
||||
b = int(data[index + 1])
|
||||
index += 2
|
||||
|
||||
if a == 0:
|
||||
sum_value = p[b]
|
||||
else:
|
||||
sum_value = p[b] - p[a - 1]
|
||||
|
||||
results.append(sum_value)
|
||||
|
||||
for result in results:
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
```
|
||||
|
@ -64,7 +64,7 @@
|
||||
|
||||

|
||||
|
||||
本图中,对所有边进行松弛,真正有效的松弛,只有松弛 边(节点1->节点2) 和 边(节点1->节点5) 。
|
||||
本图中,对所有边进行松弛,真正有效的松弛,只有松弛 边(节点1->节点2) 和 边(节点1->节点3) 。
|
||||
|
||||
而松弛 边(节点4->节点6) ,边(节点5->节点3)等等 都是无效的操作,因为 节点4 和 节点 5 都是没有被计算过的节点。
|
||||
|
||||
@ -158,16 +158,11 @@
|
||||
|
||||
边:节点5 -> 节点6,权值为-2 ,minDist[6] > minDist[5] + (-2) ,更新 minDist[6] = minDist[5] + (-2) = 3 - 2 = 1
|
||||
|
||||
如图:
|
||||
如图,将节点3加入队列,因为节点6已经在队列里,所以不用重复添加
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
因为节点3 和 节点6 都曾经加入过队列,不用重复加入,避免重复计算。
|
||||
|
||||
在代码中我们可以用一个数组 visited 来记录入过队列的元素,加入过队列的元素,不再重复入队列。
|
||||

|
||||
|
||||
所以我们在加入队列的过程可以有一个优化,**用visited数组记录已经在队列里的元素,已经在队列的元素不用重复加入**
|
||||
|
||||
--------------
|
||||
|
||||
@ -175,11 +170,12 @@
|
||||
|
||||
节点6作为终点,没有可以出发的边。
|
||||
|
||||
同理从队列中取出节点3,也没有可以出发的边
|
||||
|
||||
所以直接从队列中取出,如图:
|
||||
|
||||

|
||||
|
||||
|
||||
----------
|
||||
|
||||
这样我们就完成了基于队列优化的bellman_ford的算法模拟过程。
|
||||
@ -190,12 +186,12 @@
|
||||
|
||||
在上面模拟过程中,我们每次都要知道 一个节点作为出发点连接了哪些节点。
|
||||
|
||||
如果想方便知道这些数据,就需要使用邻接表来存储这个图,如果对于邻接表不了解的话,可以看 [kama0047.参会dijkstra堆](./kama0047.参会dijkstra堆.md) 中 图的存储 部分。
|
||||
如果想方便知道这些数据,就需要使用邻接表来存储这个图,如果对于邻接表不了解的话,可以看 [kama0047.参会dijkstra堆](./0047.参会dijkstra堆.md) 中 图的存储 部分。
|
||||
|
||||
|
||||
整体代码如下:
|
||||
|
||||
```CPP
|
||||
```
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
@ -215,7 +211,9 @@ int main() {
|
||||
int n, m, p1, p2, val;
|
||||
cin >> n >> m;
|
||||
|
||||
vector<list<Edge>> grid(n + 1); // 邻接表
|
||||
vector<list<Edge>> grid(n + 1);
|
||||
|
||||
vector<bool> isInQueue(n + 1); // 加入优化,已经在队里里的元素不用重复添加
|
||||
|
||||
// 将所有边保存起来
|
||||
for(int i = 0; i < m; i++){
|
||||
@ -230,24 +228,26 @@ int main() {
|
||||
minDist[start] = 0;
|
||||
|
||||
queue<int> que;
|
||||
que.push(start); // 队列里放入起点
|
||||
que.push(start);
|
||||
|
||||
while (!que.empty()) {
|
||||
|
||||
int node = que.front(); que.pop();
|
||||
|
||||
isInQueue[node] = false; // 从队列里取出的时候,要取消标记,我们只保证已经在队列里的元素不用重复加入
|
||||
for (Edge edge : grid[node]) {
|
||||
int from = node;
|
||||
int to = edge.to;
|
||||
int value = edge.val;
|
||||
if (minDist[to] > minDist[from] + value) { // 开始松弛
|
||||
minDist[to] = minDist[from] + value;
|
||||
que.push(to);
|
||||
minDist[to] = minDist[from] + value;
|
||||
if (isInQueue[to] == false) { // 已经在队列里的元素不用重复添加
|
||||
que.push(to);
|
||||
isInQueue[to] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (minDist[end] == INT_MAX) cout << "unconnected" << endl; // 不能到达终点
|
||||
else cout << minDist[end] << endl; // 到达终点最短路径
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ circle
|
||||
|
||||
我们拿题目中示例来画一个图:
|
||||
|
||||

|
||||

|
||||
|
||||
图中 节点1 到 节点4 的最短路径是多少(题目中的最低运输成本) (注意边可以为负数的)
|
||||
|
||||
|
@ -65,7 +65,7 @@
|
||||
|
||||

|
||||
|
||||
图中,节点2 最多已经经过2个节点 到达节点4,那么中间是有多少条边呢,是 3 条边对吧。
|
||||
图中,节点1 最多已经经过2个节点 到达节点4,那么中间是有多少条边呢,是 3 条边对吧。
|
||||
|
||||
所以本题就是求:起点最多经过k + 1 条边到达终点的最短距离。
|
||||
|
||||
|
@ -339,7 +339,7 @@ int main() {
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
## 空间优化
|
||||
|
||||
@ -426,6 +426,68 @@ floyd算法的时间复杂度相对较高,适合 稠密图且源点较多的
|
||||
|
||||
### Python
|
||||
|
||||
基于三维数组的Floyd
|
||||
|
||||
```python
|
||||
if __name__ == '__main__':
|
||||
max_int = 10005 # 设置最大路径,因为边最大距离为10^4
|
||||
|
||||
n, m = map(int, input().split())
|
||||
|
||||
grid = [[[max_int] * (n+1) for _ in range(n+1)] for _ in range(n+1)] # 初始化三维dp数组
|
||||
|
||||
for _ in range(m):
|
||||
p1, p2, w = map(int, input().split())
|
||||
grid[p1][p2][0] = w
|
||||
grid[p2][p1][0] = w
|
||||
|
||||
# 开始floyd
|
||||
for k in range(1, n+1):
|
||||
for i in range(1, n+1):
|
||||
for j in range(1, n+1):
|
||||
grid[i][j][k] = min(grid[i][j][k-1], grid[i][k][k-1] + grid[k][j][k-1])
|
||||
|
||||
# 输出结果
|
||||
z = int(input())
|
||||
for _ in range(z):
|
||||
start, end = map(int, input().split())
|
||||
if grid[start][end][n] == max_int:
|
||||
print(-1)
|
||||
else:
|
||||
print(grid[start][end][n])
|
||||
```
|
||||
|
||||
基于二维数组的Floyd
|
||||
|
||||
```python
|
||||
if __name__ == '__main__':
|
||||
max_int = 10005 # 设置最大路径,因为边最大距离为10^4
|
||||
|
||||
n, m = map(int, input().split())
|
||||
|
||||
grid = [[max_int]*(n+1) for _ in range(n+1)] # 初始化二维dp数组
|
||||
|
||||
for _ in range(m):
|
||||
p1, p2, val = map(int, input().split())
|
||||
grid[p1][p2] = val
|
||||
grid[p2][p1] = val
|
||||
|
||||
# 开始floyd
|
||||
for k in range(1, n+1):
|
||||
for i in range(1, n+1):
|
||||
for j in range(1, n+1):
|
||||
grid[i][j] = min(grid[i][j], grid[i][k] + grid[k][j])
|
||||
|
||||
# 输出结果
|
||||
z = int(input())
|
||||
for _ in range(z):
|
||||
start, end = map(int, input().split())
|
||||
if grid[start][end] == max_int:
|
||||
print(-1)
|
||||
else:
|
||||
print(grid[start][end])
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
### Rust
|
||||
|
@ -75,7 +75,7 @@
|
||||
|
||||
## 插曲
|
||||
|
||||
-------------
|
||||
-------------
|
||||
|
||||
本题和力扣 [797.所有可能的路径](https://leetcode.cn/problems/all-paths-from-source-to-target/description/) 是一样的,录友了解深度优先搜索之后,这道题目就是模板题,是送分题。
|
||||
|
||||
@ -83,7 +83,7 @@
|
||||
|
||||
如果笔试的时候出一道原题 (笔试都是ACM模式,部分面试也是ACM模式),不少熟练刷力扣的录友都难住了,因为不知道图应该怎么存,也不知道自己存的图如何去遍历。
|
||||
|
||||
所以这也是为什么我要让大家练习 ACM模式
|
||||
所以这也是为什么我要让大家练习 ACM模式,也是我为什么 在代码随想录图论讲解中,不惜自己亲自出题,让大家统一练习ACM模式。
|
||||
|
||||
--------
|
||||
|
||||
@ -422,7 +422,8 @@ int main() {
|
||||
## 其他语言版本
|
||||
|
||||
### Java
|
||||
#### 邻接矩阵写法
|
||||
|
||||
邻接矩阵写法
|
||||
```java
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -475,9 +476,9 @@ public class Main {
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
#### 邻接表写法
|
||||
邻接表写法
|
||||
```java
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
@ -533,7 +534,7 @@ public class Main {
|
||||
}
|
||||
```
|
||||
### Python
|
||||
#### 邻接矩阵写法
|
||||
邻接矩阵写法
|
||||
``` python
|
||||
def dfs(graph, x, n, path, result):
|
||||
if x == n:
|
||||
@ -564,9 +565,9 @@ def main():
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
```
|
||||
|
||||
#### 邻接表写法
|
||||
邻接表写法
|
||||
``` python
|
||||
from collections import defaultdict
|
||||
|
||||
@ -604,10 +605,268 @@ if __name__ == "__main__":
|
||||
```
|
||||
### Go
|
||||
|
||||
#### 邻接矩阵写法
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var result [][]int // 收集符合条件的路径
|
||||
var path []int // 1节点到终点的路径
|
||||
|
||||
func dfs(graph [][]int, x, n int) {
|
||||
// 当前遍历的节点x 到达节点n
|
||||
if x == n { // 找到符合条件的一条路径
|
||||
temp := make([]int, len(path))
|
||||
copy(temp, path)
|
||||
result = append(result, temp)
|
||||
return
|
||||
}
|
||||
for i := 1; i <= n; i++ { // 遍历节点x链接的所有节点
|
||||
if graph[x][i] == 1 { // 找到 x链接的节点
|
||||
path = append(path, i) // 遍历到的节点加入到路径中来
|
||||
dfs(graph, i, n) // 进入下一层递归
|
||||
path = path[:len(path)-1] // 回溯,撤销本节点
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var n, m int
|
||||
fmt.Scanf("%d %d", &n, &m)
|
||||
|
||||
// 节点编号从1到n,所以申请 n+1 这么大的数组
|
||||
graph := make([][]int, n+1)
|
||||
for i := range graph {
|
||||
graph[i] = make([]int, n+1)
|
||||
}
|
||||
|
||||
for i := 0; i < m; i++ {
|
||||
var s, t int
|
||||
fmt.Scanf("%d %d", &s, &t)
|
||||
// 使用邻接矩阵表示无向图,1 表示 s 与 t 是相连的
|
||||
graph[s][t] = 1
|
||||
}
|
||||
|
||||
path = append(path, 1) // 无论什么路径已经是从1节点出发
|
||||
dfs(graph, 1, n) // 开始遍历
|
||||
|
||||
// 输出结果
|
||||
if len(result) == 0 {
|
||||
fmt.Println(-1)
|
||||
} else {
|
||||
for _, pa := range result {
|
||||
for i := 0; i < len(pa)-1; i++ {
|
||||
fmt.Print(pa[i], " ")
|
||||
}
|
||||
fmt.Println(pa[len(pa)-1])
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 邻接表写法
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var result [][]int
|
||||
var path []int
|
||||
|
||||
func dfs(graph [][]int, x, n int) {
|
||||
if x == n {
|
||||
temp := make([]int, len(path))
|
||||
copy(temp, path)
|
||||
result = append(result, temp)
|
||||
return
|
||||
}
|
||||
for _, i := range graph[x] {
|
||||
path = append(path, i)
|
||||
dfs(graph, i, n)
|
||||
path = path[:len(path)-1]
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var n, m int
|
||||
fmt.Scanf("%d %d", &n, &m)
|
||||
|
||||
graph := make([][]int, n+1)
|
||||
for i := 0; i <= n; i++ {
|
||||
graph[i] = make([]int, 0)
|
||||
}
|
||||
|
||||
for m > 0 {
|
||||
var s, t int
|
||||
fmt.Scanf("%d %d", &s, &t)
|
||||
graph[s] = append(graph[s], t)
|
||||
m--
|
||||
}
|
||||
|
||||
path = append(path, 1)
|
||||
dfs(graph, 1, n)
|
||||
|
||||
if len(result) == 0 {
|
||||
fmt.Println(-1)
|
||||
} else {
|
||||
for _, pa := range result {
|
||||
for i := 0; i < len(pa)-1; i++ {
|
||||
fmt.Print(pa[i], " ")
|
||||
}
|
||||
fmt.Println(pa[len(pa)-1])
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Rust
|
||||
|
||||
### Javascript
|
||||
|
||||
#### 邻接矩阵写法
|
||||
|
||||
```javascript
|
||||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||||
// 创建readline接口
|
||||
let iter = r1[Symbol.asyncIterator]();
|
||||
// 创建异步迭代器
|
||||
const readline = async ()=>(await iter.next()).value;
|
||||
|
||||
|
||||
let graph;
|
||||
let N, M;
|
||||
// 收集符合条件的路径
|
||||
let result = [];
|
||||
// 1节点到终点的路径
|
||||
let path = [];
|
||||
|
||||
// 创建邻接矩阵,初始化邻接矩阵
|
||||
async function initGraph(){
|
||||
let line;
|
||||
|
||||
line = await readline();
|
||||
[N, M] = line.split(' ').map(i => parseInt(i))
|
||||
graph = new Array(N + 1).fill(0).map(() => new Array(N + 1).fill(0))
|
||||
|
||||
while(M--){
|
||||
line = await readline()
|
||||
const strArr = line ? line.split(' ').map(i => parseInt(i)) : undefined
|
||||
strArr ? graph[strArr[0]][strArr[1]] = 1 : null
|
||||
}
|
||||
};
|
||||
|
||||
// 深度搜索
|
||||
function dfs(graph, x, n){
|
||||
// 当前遍历节点为x, 到达节点为n
|
||||
if(x == n){
|
||||
result.push([...path])
|
||||
return
|
||||
}
|
||||
for(let i = 1 ; i <= n ; i++){
|
||||
if(graph[x][i] == 1){
|
||||
path.push(i)
|
||||
dfs(graph, i, n )
|
||||
path.pop(i)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(async function(){
|
||||
// 创建邻接矩阵,初始化邻接矩阵
|
||||
await initGraph();
|
||||
|
||||
// 从节点1开始深度搜索
|
||||
path.push(1);
|
||||
|
||||
// 深度搜索
|
||||
dfs(graph, 1, N );
|
||||
|
||||
// 输出
|
||||
if(result.length > 0){
|
||||
result.forEach(i => {
|
||||
console.log(i.join(' '))
|
||||
})
|
||||
}else{
|
||||
console.log(-1)
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 邻接表写法
|
||||
|
||||
```javascript
|
||||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||||
// 创建readline接口
|
||||
let iter = r1[Symbol.asyncIterator]();
|
||||
// 创建异步迭代器
|
||||
const readline = async () => (await iter.next()).value;
|
||||
|
||||
let graph;
|
||||
let N, M;
|
||||
|
||||
// 收集符合条件的路径
|
||||
let result = [];
|
||||
// 1节点到终点的路径
|
||||
let path = [];
|
||||
|
||||
// 创建邻接表,初始化邻接表
|
||||
async function initGraph() {
|
||||
let line;
|
||||
line = await readline();
|
||||
[N, M] = line.split(' ').map(i => parseInt(i))
|
||||
graph = new Array(N + 1).fill(0).map(() => new Array())
|
||||
|
||||
while (line = await readline()) {
|
||||
const strArr = line.split(' ').map(i => parseInt(i))
|
||||
strArr ? graph[strArr[0]].push(strArr[1]) : null
|
||||
}
|
||||
};
|
||||
|
||||
// 深度搜索
|
||||
async function dfs(graph, x, n) {
|
||||
// 当前遍历节点为x, 到达节点为n
|
||||
if (x == n) {
|
||||
result.push([...path])
|
||||
return
|
||||
}
|
||||
|
||||
graph[x].forEach(i => {
|
||||
path.push(i)
|
||||
dfs(graph, i, n)
|
||||
path.pop(i)
|
||||
})
|
||||
};
|
||||
|
||||
(async function () {
|
||||
// 创建邻接表,初始化邻接表
|
||||
await initGraph();
|
||||
|
||||
// 从节点1开始深度搜索
|
||||
path.push(1);
|
||||
|
||||
// 深度搜索
|
||||
dfs(graph, 1, N);
|
||||
|
||||
// 输出
|
||||
if (result.length > 0) {
|
||||
result.forEach(i => {
|
||||
console.log(i.join(' '))
|
||||
})
|
||||
} else {
|
||||
console.log(-1)
|
||||
}
|
||||
})();
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
### PhP
|
||||
@ -628,4 +887,3 @@ if __name__ == "__main__":
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -190,24 +190,309 @@ int main() {
|
||||
|
||||
### Java
|
||||
|
||||
```java
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
static int[][] dir = { {0, 1}, {1, 0}, {-1, 0}, {0, -1} }; // 四个方向
|
||||
|
||||
public static void dfs(int[][] grid, boolean[][] visited, int x, int y) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int nextx = x + dir[i][0];
|
||||
int nexty = y + dir[i][1];
|
||||
if (nextx < 0 || nextx >= grid.length || nexty < 0 || nexty >= grid[0].length) continue; // 越界了,直接跳过
|
||||
if (!visited[nextx][nexty] && grid[nextx][nexty] == 1) { // 没有访问过的 同时 是陆地的
|
||||
visited[nextx][nexty] = true;
|
||||
dfs(grid, visited, nextx, nexty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int n = scanner.nextInt();
|
||||
int m = scanner.nextInt();
|
||||
int[][] grid = new int[n][m];
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < m; j++) {
|
||||
grid[i][j] = scanner.nextInt();
|
||||
}
|
||||
}
|
||||
|
||||
boolean[][] visited = new boolean[n][m];
|
||||
|
||||
int result = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < m; j++) {
|
||||
if (!visited[i][j] && grid[i][j] == 1) {
|
||||
visited[i][j] = true;
|
||||
result++; // 遇到没访问过的陆地,+1
|
||||
dfs(grid, visited, i, j); // 将与其链接的陆地都标记上 true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
|
||||
def dfs(grid, visited, x, y):
|
||||
dir = [(0, 1), (1, 0), (-1, 0), (0, -1)] # 四个方向
|
||||
for d in dir:
|
||||
nextx, nexty = x + d[0], y + d[1]
|
||||
if 0 <= nextx < len(grid) and 0 <= nexty < len(grid[0]):
|
||||
if not visited[nextx][nexty] and grid[nextx][nexty] == 1: # 没有访问过的 同时 是陆地的
|
||||
visited[nextx][nexty] = True
|
||||
dfs(grid, visited, nextx, nexty)
|
||||
|
||||
def main():
|
||||
n, m = map(int, input().split())
|
||||
grid = [list(map(int, input().split())) for _ in range(n)]
|
||||
visited = [[False] * m for _ in range(n)]
|
||||
|
||||
result = 0
|
||||
for i in range(n):
|
||||
for j in range(m):
|
||||
if not visited[i][j] and grid[i][j] == 1:
|
||||
visited[i][j] = True
|
||||
result += 1 # 遇到没访问过的陆地,+1
|
||||
dfs(grid, visited, i, j) # 将与其链接的陆地都标记上 True
|
||||
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Go
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var dir = [4][2]int{{0, 1}, {1, 0}, {-1, 0}, {0, -1}} // 四个方向
|
||||
|
||||
func dfs(grid [][]int, visited [][]bool, x, y int) {
|
||||
for i := 0; i < 4; i++ {
|
||||
nextx := x + dir[i][0]
|
||||
nexty := y + dir[i][1]
|
||||
if nextx < 0 || nextx >= len(grid) || nexty < 0 || nexty >= len(grid[0]) {
|
||||
continue // 越界了,直接跳过
|
||||
}
|
||||
if !visited[nextx][nexty] && grid[nextx][nexty] == 1 { // 没有访问过的 同时 是陆地的
|
||||
visited[nextx][nexty] = true
|
||||
dfs(grid, visited, nextx, nexty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
var n, m int
|
||||
fmt.Scanf("%d %d", &n, &m)
|
||||
|
||||
grid := make([][]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
grid[i] = make([]int, m)
|
||||
line, _ := reader.ReadString('\n')
|
||||
line = strings.TrimSpace(line)
|
||||
elements := strings.Split(line, " ")
|
||||
for j := 0; j < m; j++ {
|
||||
grid[i][j], _ = strconv.Atoi(elements[j])
|
||||
}
|
||||
}
|
||||
|
||||
visited := make([][]bool, n)
|
||||
for i := 0; i < n; i++ {
|
||||
visited[i] = make([]bool, m)
|
||||
}
|
||||
|
||||
result := 0
|
||||
for i := 0; i < n; i++ {
|
||||
for j := 0; j < m; j++ {
|
||||
if !visited[i][j] && grid[i][j] == 1 {
|
||||
visited[i][j] = true
|
||||
result++ // 遇到没访问过的陆地,+1
|
||||
dfs(grid, visited, i, j) // 将与其链接的陆地都标记上 true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Rust
|
||||
|
||||
### Javascript
|
||||
|
||||
```javascript
|
||||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||||
// 创建readline接口
|
||||
let iter = r1[Symbol.asyncIterator]();
|
||||
// 创建异步迭代器
|
||||
const readline = async () => (await iter.next()).value;
|
||||
|
||||
let graph
|
||||
let N, M
|
||||
let visited
|
||||
let result = 0
|
||||
const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]]
|
||||
|
||||
// 读取输入,初始化地图
|
||||
const initGraph = async () => {
|
||||
let line = await readline();
|
||||
[N, M] = line.split(' ').map(Number);
|
||||
graph = new Array(N).fill(0).map(() => new Array(M).fill(0))
|
||||
visited = new Array(N).fill(false).map(() => new Array(M).fill(false))
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
line = await readline()
|
||||
line = line.split(' ').map(Number)
|
||||
for (let j = 0; j < M; j++) {
|
||||
graph[i][j] = line[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 从(x, y)开始广度优先遍历
|
||||
* @param {*} graph 地图
|
||||
* @param {*} visited 访问过的节点
|
||||
* @param {*} x 开始搜索节点的下标
|
||||
* @param {*} y 开始搜索节点的下标
|
||||
* @return {*}
|
||||
*/
|
||||
const bfs = (graph, visited, x, y) => {
|
||||
let queue = []
|
||||
queue.push([x, y])
|
||||
visited[x][y] = true //只要加入队列就立刻标记为访问过
|
||||
|
||||
while (queue.length) {
|
||||
let [x, y] = queue.shift()
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let nextx = x + dir[i][0]
|
||||
let nexty = y + dir[i][1]
|
||||
if(nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue
|
||||
if(!visited[nextx][nexty] && graph[nextx][nexty] === 1){
|
||||
queue.push([nextx, nexty])
|
||||
visited[nextx][nexty] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
(async function () {
|
||||
|
||||
// 读取输入,初始化地图
|
||||
await initGraph()
|
||||
|
||||
// 统计岛屿数
|
||||
for (let i = 0; i < N; i++) {
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (!visited[i][j] && graph[i][j] === 1) {
|
||||
// 遇到没访问过的陆地,+1
|
||||
result++
|
||||
|
||||
// 广度优先遍历,将相邻陆地标记为已访问
|
||||
bfs(graph, visited, i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(result);
|
||||
})()
|
||||
```
|
||||
|
||||
|
||||
|
||||
### TypeScript
|
||||
|
||||
### PhP
|
||||
|
||||
```PHP
|
||||
|
||||
<?php
|
||||
|
||||
function dfs($grid, &$visited, $x, $y) {
|
||||
$dir = [[0, 1], [1, 0], [-1, 0], [0, -1]]; // 四个方向
|
||||
foreach ($dir as $d) {
|
||||
$nextx = $x + $d[0];
|
||||
$nexty = $y + $d[1];
|
||||
if ($nextx < 0 || $nextx >= count($grid) || $nexty < 0 || $nexty >= count($grid[0])) {
|
||||
continue; // 越界了,直接跳过
|
||||
}
|
||||
if (!$visited[$nextx][$nexty] && $grid[$nextx][$nexty] == 1) { // 没有访问过的 同时 是陆地的
|
||||
$visited[$nextx][$nexty] = true;
|
||||
dfs($grid, $visited, $nextx, $nexty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
fscanf(STDIN, "%d %d", $n, $m);
|
||||
$grid = [];
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
$grid[$i] = array_map('intval', explode(' ', trim(fgets(STDIN))));
|
||||
}
|
||||
|
||||
$visited = array_fill(0, $n, array_fill(0, $m, false));
|
||||
|
||||
$result = 0;
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
for ($j = 0; $j < $m; $j++) {
|
||||
if (!$visited[$i][$j] && $grid[$i][$j] == 1) {
|
||||
$visited[$i][$j] = true;
|
||||
$result++; // 遇到没访问过的陆地,+1
|
||||
dfs($grid, $visited, $i, $j); // 将与其链接的陆地都标记上 true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo $result . PHP_EOL;
|
||||
}
|
||||
|
||||
main();
|
||||
?>
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Swift
|
||||
|
||||
### Scala
|
||||
|
||||
### C#
|
||||
|
||||
|
||||
### Dart
|
||||
|
||||
### C
|
||||
|
@ -185,12 +185,181 @@ int main() {
|
||||
|
||||
### Python
|
||||
|
||||
版本一
|
||||
|
||||
```python
|
||||
direction = [[0, 1], [1, 0], [0, -1], [-1, 0]] # 四个方向:上、右、下、左
|
||||
|
||||
|
||||
def dfs(grid, visited, x, y):
|
||||
"""
|
||||
对一块陆地进行深度优先遍历并标记
|
||||
"""
|
||||
for i, j in direction:
|
||||
next_x = x + i
|
||||
next_y = y + j
|
||||
# 下标越界,跳过
|
||||
if next_x < 0 or next_x >= len(grid) or next_y < 0 or next_y >= len(grid[0]):
|
||||
continue
|
||||
# 未访问的陆地,标记并调用深度优先搜索
|
||||
if not visited[next_x][next_y] and grid[next_x][next_y] == 1:
|
||||
visited[next_x][next_y] = True
|
||||
dfs(grid, visited, next_x, next_y)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 版本一
|
||||
n, m = map(int, input().split())
|
||||
|
||||
# 邻接矩阵
|
||||
grid = []
|
||||
for i in range(n):
|
||||
grid.append(list(map(int, input().split())))
|
||||
|
||||
# 访问表
|
||||
visited = [[False] * m for _ in range(n)]
|
||||
|
||||
res = 0
|
||||
for i in range(n):
|
||||
for j in range(m):
|
||||
# 判断:如果当前节点是陆地,res+1并标记访问该节点,使用深度搜索标记相邻陆地。
|
||||
if grid[i][j] == 1 and not visited[i][j]:
|
||||
res += 1
|
||||
visited[i][j] = True
|
||||
dfs(grid, visited, i, j)
|
||||
|
||||
print(res)
|
||||
```
|
||||
|
||||
版本二
|
||||
|
||||
```python
|
||||
direction = [[0, 1], [1, 0], [0, -1], [-1, 0]] # 四个方向:上、右、下、左
|
||||
|
||||
|
||||
def dfs(grid, visited, x, y):
|
||||
"""
|
||||
对一块陆地进行深度优先遍历并标记
|
||||
"""
|
||||
# 与版本一的差别,在调用前增加判断终止条件
|
||||
if visited[x][y] or grid[x][y] == 0:
|
||||
return
|
||||
visited[x][y] = True
|
||||
|
||||
for i, j in direction:
|
||||
next_x = x + i
|
||||
next_y = y + j
|
||||
# 下标越界,跳过
|
||||
if next_x < 0 or next_x >= len(grid) or next_y < 0 or next_y >= len(grid[0]):
|
||||
continue
|
||||
# 由于判断条件放在了方法首部,此处直接调用dfs方法
|
||||
dfs(grid, visited, next_x, next_y)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 版本二
|
||||
n, m = map(int, input().split())
|
||||
|
||||
# 邻接矩阵
|
||||
grid = []
|
||||
for i in range(n):
|
||||
grid.append(list(map(int, input().split())))
|
||||
|
||||
# 访问表
|
||||
visited = [[False] * m for _ in range(n)]
|
||||
|
||||
res = 0
|
||||
for i in range(n):
|
||||
for j in range(m):
|
||||
# 判断:如果当前节点是陆地,res+1并标记访问该节点,使用深度搜索标记相邻陆地。
|
||||
if grid[i][j] == 1 and not visited[i][j]:
|
||||
res += 1
|
||||
dfs(grid, visited, i, j)
|
||||
|
||||
print(res)
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
### Rust
|
||||
|
||||
### Javascript
|
||||
|
||||
```javascript
|
||||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||||
// 创建readline接口
|
||||
let iter = r1[Symbol.asyncIterator]();
|
||||
// 创建异步迭代器
|
||||
const readline = async () => (await iter.next()).value;
|
||||
|
||||
let graph
|
||||
let N, M
|
||||
let visited
|
||||
let result = 0
|
||||
const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]]
|
||||
|
||||
// 读取输入,初始化地图
|
||||
const initGraph = async () => {
|
||||
let line = await readline();
|
||||
[N, M] = line.split(' ').map(Number);
|
||||
graph = new Array(N).fill(0).map(() => new Array(M).fill(0))
|
||||
visited = new Array(N).fill(false).map(() => new Array(M).fill(false))
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
line = await readline()
|
||||
line = line.split(' ').map(Number)
|
||||
for (let j = 0; j < M; j++) {
|
||||
graph[i][j] = line[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 从节点x,y开始深度优先遍历
|
||||
* @param {*} graph 是地图,也就是一个二维数组
|
||||
* @param {*} visited 标记访问过的节点,不要重复访问
|
||||
* @param {*} x 表示开始搜索节点的下标
|
||||
* @param {*} y 表示开始搜索节点的下标
|
||||
* @return {*}
|
||||
*/
|
||||
const dfs = (graph, visited, x, y) => {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const nextx = x + dir[i][0]
|
||||
const nexty = y + dir[i][1]
|
||||
if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue
|
||||
if (!visited[nextx][nexty] && graph[nextx][nexty] === 1) {
|
||||
visited[nextx][nexty] = true
|
||||
dfs(graph, visited, nextx, nexty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(async function () {
|
||||
|
||||
// 读取输入,初始化地图
|
||||
await initGraph()
|
||||
|
||||
// 统计岛屿数
|
||||
for (let i = 0; i < N; i++) {
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (!visited[i][j] && graph[i][j] === 1) {
|
||||
// 标记已访问
|
||||
visited[i][j] = true
|
||||
|
||||
// 遇到没访问过的陆地,+1
|
||||
result++
|
||||
|
||||
// 深度优先遍历,将相邻陆地标记为已访问
|
||||
dfs(graph, visited, i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(result);
|
||||
})()
|
||||
```
|
||||
|
||||
|
||||
|
||||
### TypeScript
|
||||
|
||||
### PhP
|
||||
|
@ -226,16 +226,325 @@ public:
|
||||
|
||||
### Python
|
||||
|
||||
DFS
|
||||
|
||||
```python
|
||||
# 四个方向
|
||||
position = [[0, 1], [1, 0], [0, -1], [-1, 0]]
|
||||
count = 0
|
||||
|
||||
|
||||
def dfs(grid, visited, x, y):
|
||||
"""
|
||||
深度优先搜索,对一整块陆地进行标记
|
||||
"""
|
||||
global count # 定义全局变量,便于传递count值
|
||||
for i, j in position:
|
||||
cur_x = x + i
|
||||
cur_y = y + j
|
||||
# 下标越界,跳过
|
||||
if cur_x < 0 or cur_x >= len(grid) or cur_y < 0 or cur_y >= len(grid[0]):
|
||||
continue
|
||||
if not visited[cur_x][cur_y] and grid[cur_x][cur_y] == 1:
|
||||
visited[cur_x][cur_y] = True
|
||||
count += 1
|
||||
dfs(grid, visited, cur_x, cur_y)
|
||||
|
||||
|
||||
n, m = map(int, input().split())
|
||||
# 邻接矩阵
|
||||
grid = []
|
||||
for i in range(n):
|
||||
grid.append(list(map(int, input().split())))
|
||||
# 访问表
|
||||
visited = [[False] * m for _ in range(n)]
|
||||
|
||||
result = 0 # 记录最终结果
|
||||
for i in range(n):
|
||||
for j in range(m):
|
||||
if grid[i][j] == 1 and not visited[i][j]:
|
||||
count = 1
|
||||
visited[i][j] = True
|
||||
dfs(grid, visited, i, j)
|
||||
result = max(count, result)
|
||||
|
||||
print(result)
|
||||
```
|
||||
|
||||
BFS
|
||||
|
||||
```python
|
||||
from collections import deque
|
||||
|
||||
position = [[0, 1], [1, 0], [0, -1], [-1, 0]] # 四个方向
|
||||
count = 0
|
||||
|
||||
|
||||
def bfs(grid, visited, x, y):
|
||||
"""
|
||||
广度优先搜索对陆地进行标记
|
||||
"""
|
||||
global count # 声明全局变量
|
||||
que = deque()
|
||||
que.append([x, y])
|
||||
while que:
|
||||
cur_x, cur_y = que.popleft()
|
||||
for i, j in position:
|
||||
next_x = cur_x + i
|
||||
next_y = cur_y + j
|
||||
# 下标越界,跳过
|
||||
if next_x < 0 or next_x >= len(grid) or next_y < 0 or next_y >= len(grid[0]):
|
||||
continue
|
||||
if grid[next_x][next_y] == 1 and not visited[next_x][next_y]:
|
||||
visited[next_x][next_y] = True
|
||||
count += 1
|
||||
que.append([next_x, next_y])
|
||||
|
||||
|
||||
n, m = map(int, input().split())
|
||||
# 邻接矩阵
|
||||
grid = []
|
||||
for i in range(n):
|
||||
grid.append(list(map(int, input().split())))
|
||||
visited = [[False] * m for _ in range(n)] # 访问表
|
||||
|
||||
result = 0 # 记录最终结果
|
||||
for i in range(n):
|
||||
for j in range(m):
|
||||
if grid[i][j] == 1 and not visited[i][j]:
|
||||
count = 1
|
||||
visited[i][j] = True
|
||||
bfs(grid, visited, i, j)
|
||||
res = max(result, count)
|
||||
|
||||
print(result)
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
``` go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var count int
|
||||
var dir = [][]int{{0, 1}, {1, 0}, {-1, 0}, {0, -1}} // 四个方向
|
||||
|
||||
func dfs(grid [][]int, visited [][]bool, x, y int) {
|
||||
for i := 0; i < 4; i++ {
|
||||
nextx := x + dir[i][0]
|
||||
nexty := y + dir[i][1]
|
||||
if nextx < 0 || nextx >= len(grid) || nexty < 0 || nexty >= len(grid[0]) {
|
||||
continue // 越界了,直接跳过
|
||||
}
|
||||
if !visited[nextx][nexty] && grid[nextx][nexty] == 1 { // 没有访问过的 同时 是陆地的
|
||||
visited[nextx][nexty] = true
|
||||
count++
|
||||
dfs(grid, visited, nextx, nexty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var n, m int
|
||||
fmt.Scan(&n, &m)
|
||||
|
||||
grid := make([][]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
grid[i] = make([]int, m)
|
||||
for j := 0; j < m; j++ {
|
||||
fmt.Scan(&grid[i][j])
|
||||
}
|
||||
}
|
||||
|
||||
visited := make([][]bool, n)
|
||||
for i := 0; i < n; i++ {
|
||||
visited[i] = make([]bool, m)
|
||||
}
|
||||
|
||||
result := 0
|
||||
for i := 0; i < n; i++ {
|
||||
for j := 0; j < m; j++ {
|
||||
if !visited[i][j] && grid[i][j] == 1 {
|
||||
count = 1 // 因为dfs处理下一个节点,所以这里遇到陆地了就先计数,dfs处理接下来的相邻陆地
|
||||
visited[i][j] = true
|
||||
dfs(grid, visited, i, j)
|
||||
if count > result {
|
||||
result = count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Rust
|
||||
|
||||
### Javascript
|
||||
|
||||
```javascript
|
||||
// 广搜版
|
||||
|
||||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||||
// 创建readline接口
|
||||
let iter = r1[Symbol.asyncIterator]();
|
||||
// 创建异步迭代器
|
||||
const readline = async () => (await iter.next()).value;
|
||||
|
||||
let graph // 地图
|
||||
let N, M // 地图大小
|
||||
let visited // 访问过的节点
|
||||
let result = 0 // 最大岛屿面积
|
||||
let count = 0 // 岛屿内节点数
|
||||
const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向
|
||||
|
||||
|
||||
// 读取输入,初始化地图
|
||||
const initGraph = async () => {
|
||||
let line = await readline();
|
||||
[N, M] = line.split(' ').map(Number);
|
||||
graph = new Array(N).fill(0).map(() => new Array(M).fill(0))
|
||||
visited = new Array(N).fill(false).map(() => new Array(M).fill(false))
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
line = await readline()
|
||||
line = line.split(' ').map(Number)
|
||||
for (let j = 0; j < M; j++) {
|
||||
graph[i][j] = line[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 从(x, y)开始广度优先遍历
|
||||
* @param {*} graph 地图
|
||||
* @param {*} visited 访问过的节点
|
||||
* @param {*} x 开始搜索节点的下标
|
||||
* @param {*} y 开始搜索节点的下标
|
||||
* @return {*}
|
||||
*/
|
||||
const bfs = (graph, visited, x, y) => {
|
||||
let queue = []
|
||||
queue.push([x, y])
|
||||
count++
|
||||
visited[x][y] = true //只要加入队列就立刻标记为访问过
|
||||
|
||||
while (queue.length) {
|
||||
let [xx, yy] = queue.shift()
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let nextx = xx + dir[i][0]
|
||||
let nexty = yy + dir[i][1]
|
||||
if(nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue
|
||||
if(!visited[nextx][nexty] && graph[nextx][nexty] === 1){
|
||||
queue.push([nextx, nexty])
|
||||
count++
|
||||
visited[nextx][nexty] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
(async function () {
|
||||
|
||||
// 读取输入,初始化地图
|
||||
await initGraph()
|
||||
|
||||
// 统计最大岛屿面积
|
||||
for (let i = 0; i < N; i++) {
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (!visited[i][j] && graph[i][j] === 1) { //遇到没有访问过的陆地
|
||||
// 重新计算面积
|
||||
count = 0
|
||||
|
||||
// 广度优先遍历,统计岛屿内节点数,并将岛屿标记为已访问
|
||||
bfs(graph, visited, i, j)
|
||||
|
||||
// 更新最大岛屿面积
|
||||
result = Math.max(result, count)
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(result);
|
||||
})()
|
||||
```
|
||||
|
||||
|
||||
|
||||
### TypeScript
|
||||
|
||||
### PhP
|
||||
|
||||
``` php
|
||||
|
||||
<?php
|
||||
|
||||
function dfs(&$grid, &$visited, $x, $y, &$count, &$dir) {
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$nextx = $x + $dir[$i][0];
|
||||
$nexty = $y + $dir[$i][1];
|
||||
if ($nextx < 0 || $nextx >= count($grid) || $nexty < 0 || $nexty >= count($grid[0])) continue; // 越界了,直接跳过
|
||||
if (!$visited[$nextx][$nexty] && $grid[$nextx][$nexty] == 1) { // 没有访问过的 同时 是陆地的
|
||||
$visited[$nextx][$nexty] = true;
|
||||
$count++;
|
||||
dfs($grid, $visited, $nextx, $nexty, $count, $dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Main function
|
||||
function main() {
|
||||
$input = trim(fgets(STDIN));
|
||||
list($n, $m) = explode(' ', $input);
|
||||
|
||||
$grid = [];
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
$input = trim(fgets(STDIN));
|
||||
$grid[] = array_map('intval', explode(' ', $input));
|
||||
}
|
||||
|
||||
$visited = [];
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
$visited[] = array_fill(0, $m, false);
|
||||
}
|
||||
|
||||
$result = 0;
|
||||
$count = 0;
|
||||
$dir = [[0, 1], [1, 0], [-1, 0], [0, -1]]; // 四个方向
|
||||
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
for ($j = 0; $j < $m; $j++) {
|
||||
if (!$visited[$i][$j] && $grid[$i][$j] == 1) {
|
||||
$count = 1; // 因为dfs处理下一个节点,所以这里遇到陆地了就先计数,dfs处理接下来的相邻陆地
|
||||
$visited[$i][$j] = true;
|
||||
dfs($grid, $visited, $i, $j, $count, $dir); // 将与其链接的陆地都标记上 true
|
||||
$result = max($result, $count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo $result . "\n";
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
?>
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Swift
|
||||
|
||||
### Scala
|
||||
|
@ -185,6 +185,77 @@ int main() {
|
||||
|
||||
### Java
|
||||
|
||||
``` java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private static int count = 0;
|
||||
private static final int[][] dir = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; // 四个方向
|
||||
|
||||
private static void bfs(int[][] grid, int x, int y) {
|
||||
Queue<int[]> que = new LinkedList<>();
|
||||
que.add(new int[]{x, y});
|
||||
grid[x][y] = 0; // 只要加入队列,立刻标记
|
||||
count++;
|
||||
while (!que.isEmpty()) {
|
||||
int[] cur = que.poll();
|
||||
int curx = cur[0];
|
||||
int cury = cur[1];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int nextx = curx + dir[i][0];
|
||||
int nexty = cury + dir[i][1];
|
||||
if (nextx < 0 || nextx >= grid.length || nexty < 0 || nexty >= grid[0].length) continue; // 越界了,直接跳过
|
||||
if (grid[nextx][nexty] == 1) {
|
||||
que.add(new int[]{nextx, nexty});
|
||||
count++;
|
||||
grid[nextx][nexty] = 0; // 只要加入队列立刻标记
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int n = scanner.nextInt();
|
||||
int m = scanner.nextInt();
|
||||
int[][] grid = new int[n][m];
|
||||
|
||||
// 读取网格
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < m; j++) {
|
||||
grid[i][j] = scanner.nextInt();
|
||||
}
|
||||
}
|
||||
|
||||
// 从左侧边,和右侧边向中间遍历
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (grid[i][0] == 1) bfs(grid, i, 0);
|
||||
if (grid[i][m - 1] == 1) bfs(grid, i, m - 1);
|
||||
}
|
||||
|
||||
// 从上边和下边向中间遍历
|
||||
for (int j = 0; j < m; j++) {
|
||||
if (grid[0][j] == 1) bfs(grid, 0, j);
|
||||
if (grid[n - 1][j] == 1) bfs(grid, n - 1, j);
|
||||
}
|
||||
|
||||
count = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < m; j++) {
|
||||
if (grid[i][j] == 1) bfs(grid, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Python
|
||||
```python
|
||||
from collections import deque
|
||||
@ -238,10 +309,268 @@ print(count)
|
||||
```
|
||||
### Go
|
||||
|
||||
``` go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var count int
|
||||
var dir = [4][2]int{{0, 1}, {1, 0}, {-1, 0}, {0, -1}} // 四个方向
|
||||
|
||||
func bfs(grid [][]int, x, y int) {
|
||||
queue := [][2]int{{x, y}}
|
||||
grid[x][y] = 0 // 只要加入队列,立刻标记
|
||||
count++
|
||||
|
||||
for len(queue) > 0 {
|
||||
cur := queue[0]
|
||||
queue = queue[1:]
|
||||
curx, cury := cur[0], cur[1]
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
nextx := curx + dir[i][0]
|
||||
nexty := cury + dir[i][1]
|
||||
|
||||
if nextx < 0 || nextx >= len(grid) || nexty < 0 || nexty >= len(grid[0]) {
|
||||
continue // 越界了,直接跳过
|
||||
}
|
||||
|
||||
if grid[nextx][nexty] == 1 {
|
||||
queue = append(queue, [2]int{nextx, nexty})
|
||||
count++
|
||||
grid[nextx][nexty] = 0 // 只要加入队列立刻标记
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var n, m int
|
||||
fmt.Scan(&n, &m)
|
||||
|
||||
grid := make([][]int, n)
|
||||
for i := range grid {
|
||||
grid[i] = make([]int, m)
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
for j := 0; j < m; j++ {
|
||||
fmt.Scan(&grid[i][j])
|
||||
}
|
||||
}
|
||||
|
||||
// 从左侧边,和右侧边向中间遍历
|
||||
for i := 0; i < n; i++ {
|
||||
if grid[i][0] == 1 {
|
||||
bfs(grid, i, 0)
|
||||
}
|
||||
if grid[i][m-1] == 1 {
|
||||
bfs(grid, i, m-1)
|
||||
}
|
||||
}
|
||||
|
||||
// 从上边和下边向中间遍历
|
||||
for j := 0; j < m; j++ {
|
||||
if grid[0][j] == 1 {
|
||||
bfs(grid, 0, j)
|
||||
}
|
||||
if grid[n-1][j] == 1 {
|
||||
bfs(grid, n-1, j)
|
||||
}
|
||||
}
|
||||
|
||||
// 清空之前的计数
|
||||
count = 0
|
||||
|
||||
// 遍历所有位置
|
||||
for i := 0; i < n; i++ {
|
||||
for j := 0; j < m; j++ {
|
||||
if grid[i][j] == 1 {
|
||||
bfs(grid, i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(count)
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
### Rust
|
||||
|
||||
### Javascript
|
||||
|
||||
#### 深搜版
|
||||
|
||||
```javascript
|
||||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||||
// 创建readline接口
|
||||
let iter = r1[Symbol.asyncIterator]();
|
||||
// 创建异步迭代器
|
||||
const readline = async () => (await iter.next()).value;
|
||||
|
||||
let graph // 地图
|
||||
let N, M // 地图大小
|
||||
let count = 0 // 孤岛的总面积
|
||||
const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向
|
||||
|
||||
|
||||
// 读取输入,初始化地图
|
||||
const initGraph = async () => {
|
||||
let line = await readline();
|
||||
[N, M] = line.split(' ').map(Number);
|
||||
graph = new Array(N).fill(0).map(() => new Array(M).fill(0))
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
line = await readline()
|
||||
line = line.split(' ').map(Number)
|
||||
for (let j = 0; j < M; j++) {
|
||||
graph[i][j] = line[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 从(x,y)开始深度优先遍历地图
|
||||
* @param {*} graph 地图
|
||||
* @param {*} x 开始搜索节点的下标
|
||||
* @param {*} y 开始搜索节点的下标
|
||||
* @return {*}
|
||||
*/
|
||||
const dfs = (graph, x, y) => {
|
||||
if(graph[x][y] === 0) return
|
||||
graph[x][y] = 0 // 标记为海洋
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let nextx = x + dir[i][0]
|
||||
let nexty = y + dir[i][1]
|
||||
if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue
|
||||
dfs(graph, nextx, nexty)
|
||||
}
|
||||
}
|
||||
|
||||
(async function () {
|
||||
|
||||
// 读取输入,初始化地图
|
||||
await initGraph()
|
||||
|
||||
// 遍历地图左右两边
|
||||
for (let i = 0; i < N; i++) {
|
||||
if (graph[i][0] === 1) dfs(graph, i, 0)
|
||||
if (graph[i][M - 1] === 1) dfs(graph, i, M - 1)
|
||||
}
|
||||
|
||||
// 遍历地图上下两边
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (graph[0][j] === 1) dfs(graph, 0, j)
|
||||
if (graph[N - 1][j] === 1) dfs(graph, N - 1, j)
|
||||
}
|
||||
|
||||
count = 0
|
||||
// 统计孤岛的总面积
|
||||
for (let i = 0; i < N; i++) {
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (graph[i][j] === 1) count++
|
||||
}
|
||||
}
|
||||
console.log(count);
|
||||
})()
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 广搜版
|
||||
|
||||
```javascript
|
||||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||||
// 创建readline接口
|
||||
let iter = r1[Symbol.asyncIterator]();
|
||||
// 创建异步迭代器
|
||||
const readline = async () => (await iter.next()).value;
|
||||
|
||||
let graph // 地图
|
||||
let N, M // 地图大小
|
||||
let count = 0 // 孤岛的总面积
|
||||
const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向
|
||||
|
||||
|
||||
// 读取输入,初始化地图
|
||||
const initGraph = async () => {
|
||||
let line = await readline();
|
||||
[N, M] = line.split(' ').map(Number);
|
||||
graph = new Array(N).fill(0).map(() => new Array(M).fill(0))
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
line = await readline()
|
||||
line = line.split(' ').map(Number)
|
||||
for (let j = 0; j < M; j++) {
|
||||
graph[i][j] = line[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 从(x,y)开始广度优先遍历地图
|
||||
* @param {*} graph 地图
|
||||
* @param {*} x 开始搜索节点的下标
|
||||
* @param {*} y 开始搜索节点的下标
|
||||
* @return {*}
|
||||
*/
|
||||
const bfs = (graph, x, y) => {
|
||||
let queue = []
|
||||
queue.push([x, y])
|
||||
graph[x][y] = 0 // 只要加入队列,立刻标记
|
||||
|
||||
while (queue.length) {
|
||||
let [xx, yy] = queue.shift()
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let nextx = xx + dir[i][0]
|
||||
let nexty = yy + dir[i][1]
|
||||
if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue
|
||||
if (graph[nextx][nexty] === 1) {
|
||||
queue.push([nextx, nexty])
|
||||
graph[nextx][nexty] = 0 // 只要加入队列,立刻标记
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
(async function () {
|
||||
|
||||
// 读取输入,初始化地图
|
||||
await initGraph()
|
||||
|
||||
// 遍历地图左右两边
|
||||
for (let i = 0; i < N; i++) {
|
||||
if (graph[i][0] === 1) bfs(graph, i, 0)
|
||||
if (graph[i][M - 1] === 1) bfs(graph, i, M - 1)
|
||||
}
|
||||
|
||||
// 遍历地图上下两边
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (graph[0][j] === 1) bfs(graph, 0, j)
|
||||
if (graph[N - 1][j] === 1) bfs(graph, N - 1, j)
|
||||
}
|
||||
|
||||
count = 0
|
||||
// 统计孤岛的总面积
|
||||
for (let i = 0; i < N; i++) {
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (graph[i][j] === 1) count++
|
||||
}
|
||||
}
|
||||
console.log(count);
|
||||
})()
|
||||
```
|
||||
|
||||
|
||||
|
||||
### TypeScript
|
||||
|
||||
### PhP
|
||||
|
@ -146,6 +146,171 @@ int main() {
|
||||
|
||||
### Javascript
|
||||
|
||||
#### 深搜版
|
||||
|
||||
```javascript
|
||||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||||
// 创建readline接口
|
||||
let iter = r1[Symbol.asyncIterator]();
|
||||
// 创建异步迭代器
|
||||
const readline = async () => (await iter.next()).value;
|
||||
|
||||
let graph // 地图
|
||||
let N, M // 地图大小
|
||||
const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向
|
||||
|
||||
|
||||
// 读取输入,初始化地图
|
||||
const initGraph = async () => {
|
||||
let line = await readline();
|
||||
[N, M] = line.split(' ').map(Number);
|
||||
graph = new Array(N).fill(0).map(() => new Array(M).fill(0))
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
line = await readline()
|
||||
line = line.split(' ').map(Number)
|
||||
for (let j = 0; j < M; j++) {
|
||||
graph[i][j] = line[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 从(x,y)开始深度优先遍历地图
|
||||
* @param {*} graph 地图
|
||||
* @param {*} x 开始搜索节点的下标
|
||||
* @param {*} y 开始搜索节点的下标
|
||||
* @return {*}
|
||||
*/
|
||||
const dfs = (graph, x, y) => {
|
||||
if (graph[x][y] !== 1) return
|
||||
graph[x][y] = 2 // 标记为非孤岛陆地
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let nextx = x + dir[i][0]
|
||||
let nexty = y + dir[i][1]
|
||||
if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue
|
||||
dfs(graph, nextx, nexty)
|
||||
}
|
||||
}
|
||||
|
||||
(async function () {
|
||||
|
||||
// 读取输入,初始化地图
|
||||
await initGraph()
|
||||
|
||||
// 遍历地图左右两边
|
||||
for (let i = 0; i < N; i++) {
|
||||
if (graph[i][0] === 1) dfs(graph, i, 0)
|
||||
if (graph[i][M - 1] === 1) dfs(graph, i, M - 1)
|
||||
}
|
||||
|
||||
// 遍历地图上下两边
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (graph[0][j] === 1) dfs(graph, 0, j)
|
||||
if (graph[N - 1][j] === 1) dfs(graph, N - 1, j)
|
||||
}
|
||||
|
||||
|
||||
// 遍历地图,将孤岛沉没,即将陆地1标记为0;将非孤岛陆地2标记为1
|
||||
for (let i = 0; i < N; i++) {
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (graph[i][j] === 1) graph[i][j] = 0
|
||||
else if (graph[i][j] === 2) graph[i][j] = 1
|
||||
}
|
||||
console.log(graph[i].join(' '));
|
||||
}
|
||||
})()
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 广搜版
|
||||
|
||||
```javascript
|
||||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||||
// 创建readline接口
|
||||
let iter = r1[Symbol.asyncIterator]();
|
||||
// 创建异步迭代器
|
||||
const readline = async () => (await iter.next()).value;
|
||||
|
||||
let graph // 地图
|
||||
let N, M // 地图大小
|
||||
const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向
|
||||
|
||||
|
||||
// 读取输入,初始化地图
|
||||
const initGraph = async () => {
|
||||
let line = await readline();
|
||||
[N, M] = line.split(' ').map(Number);
|
||||
graph = new Array(N).fill(0).map(() => new Array(M).fill(0))
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
line = await readline()
|
||||
line = line.split(' ').map(Number)
|
||||
for (let j = 0; j < M; j++) {
|
||||
graph[i][j] = line[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 从(x,y)开始广度优先遍历地图
|
||||
* @param {*} graph 地图
|
||||
* @param {*} x 开始搜索节点的下标
|
||||
* @param {*} y 开始搜索节点的下标
|
||||
* @return {*}
|
||||
*/
|
||||
const bfs = (graph, x, y) => {
|
||||
let queue = []
|
||||
queue.push([x, y])
|
||||
graph[x][y] = 2 // 标记为非孤岛陆地
|
||||
|
||||
while (queue.length) {
|
||||
let [xx, yy] = queue.shift()
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let nextx = xx + dir[i][0]
|
||||
let nexty = yy + dir[i][1]
|
||||
if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue
|
||||
if (graph[nextx][nexty] === 1) bfs(graph, nextx, nexty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(async function () {
|
||||
|
||||
// 读取输入,初始化地图
|
||||
await initGraph()
|
||||
|
||||
// 遍历地图左右两边
|
||||
for (let i = 0; i < N; i++) {
|
||||
if (graph[i][0] === 1) bfs(graph, i, 0)
|
||||
if (graph[i][M - 1] === 1) bfs(graph, i, M - 1)
|
||||
}
|
||||
|
||||
// 遍历地图上下两边
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (graph[0][j] === 1) bfs(graph, 0, j)
|
||||
if (graph[N - 1][j] === 1) bfs(graph, N - 1, j)
|
||||
}
|
||||
|
||||
|
||||
// 遍历地图,将孤岛沉没,即将陆地1标记为0;将非孤岛陆地2标记为1
|
||||
for (let i = 0; i < N; i++) {
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (graph[i][j] === 1) graph[i][j] = 0
|
||||
else if (graph[i][j] === 2) graph[i][j] = 1
|
||||
}
|
||||
console.log(graph[i].join(' '));
|
||||
}
|
||||
})()
|
||||
```
|
||||
|
||||
|
||||
|
||||
### TypeScript
|
||||
|
||||
### PhP
|
||||
|
@ -281,8 +281,136 @@ for (int j = 0; j < m; j++) {
|
||||
## 其他语言版本
|
||||
|
||||
### Java
|
||||
```Java
|
||||
public class Main {
|
||||
|
||||
// 采用 DFS 进行搜索
|
||||
public static void dfs(int[][] heights, int x, int y, boolean[][] visited, int preH) {
|
||||
// 遇到边界或者访问过的点,直接返回
|
||||
if (x < 0 || x >= heights.length || y < 0 || y >= heights[0].length || visited[x][y]) return;
|
||||
// 不满足水流入条件的直接返回
|
||||
if (heights[x][y] < preH) return;
|
||||
// 满足条件,设置为true,表示可以从边界到达此位置
|
||||
visited[x][y] = true;
|
||||
|
||||
// 向下一层继续搜索
|
||||
dfs(heights, x + 1, y, visited, heights[x][y]);
|
||||
dfs(heights, x - 1, y, visited, heights[x][y]);
|
||||
dfs(heights, x, y + 1, visited, heights[x][y]);
|
||||
dfs(heights, x, y - 1, visited, heights[x][y]);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Scanner sc = new Scanner(System.in);
|
||||
int m = sc.nextInt();
|
||||
int n = sc.nextInt();
|
||||
|
||||
int[][] heights = new int[m][n];
|
||||
for (int i = 0; i < m; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
heights[i][j] = sc.nextInt();
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化两个二位boolean数组,代表两个边界
|
||||
boolean[][] pacific = new boolean[m][n];
|
||||
boolean[][] atlantic = new boolean[m][n];
|
||||
|
||||
// 从左右边界出发进行DFS
|
||||
for (int i = 0; i < m; i++) {
|
||||
dfs(heights, i, 0, pacific, Integer.MIN_VALUE);
|
||||
dfs(heights, i, n - 1, atlantic, Integer.MIN_VALUE);
|
||||
}
|
||||
|
||||
// 从上下边界出发进行DFS
|
||||
for (int j = 0; j < n; j++) {
|
||||
dfs(heights, 0, j, pacific, Integer.MIN_VALUE);
|
||||
dfs(heights, m - 1, j, atlantic, Integer.MIN_VALUE);
|
||||
}
|
||||
|
||||
// 当两个边界二维数组在某个位置都为true时,符合题目要求
|
||||
List<List<Integer>> res = new ArrayList<>();
|
||||
for (int i = 0; i < m; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
if (pacific[i][j] && atlantic[i][j]) {
|
||||
res.add(Arrays.asList(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 打印结果
|
||||
for (List<Integer> list : res) {
|
||||
for (int k = 0; k < list.size(); k++) {
|
||||
if (k == 0) {
|
||||
System.out.print(list.get(k) + " ");
|
||||
} else {
|
||||
System.out.print(list.get(k));
|
||||
}
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Python
|
||||
```Python
|
||||
first = set()
|
||||
second = set()
|
||||
directions = [[-1, 0], [0, 1], [1, 0], [0, -1]]
|
||||
|
||||
def dfs(i, j, graph, visited, side):
|
||||
if visited[i][j]:
|
||||
return
|
||||
|
||||
visited[i][j] = True
|
||||
side.add((i, j))
|
||||
|
||||
for x, y in directions:
|
||||
new_x = i + x
|
||||
new_y = j + y
|
||||
if (
|
||||
0 <= new_x < len(graph)
|
||||
and 0 <= new_y < len(graph[0])
|
||||
and int(graph[new_x][new_y]) >= int(graph[i][j])
|
||||
):
|
||||
dfs(new_x, new_y, graph, visited, side)
|
||||
|
||||
def main():
|
||||
global first
|
||||
global second
|
||||
|
||||
N, M = map(int, input().strip().split())
|
||||
graph = []
|
||||
for _ in range(N):
|
||||
row = input().strip().split()
|
||||
graph.append(row)
|
||||
|
||||
# 是否可到达第一边界
|
||||
visited = [[False] * M for _ in range(N)]
|
||||
for i in range(M):
|
||||
dfs(0, i, graph, visited, first)
|
||||
for i in range(N):
|
||||
dfs(i, 0, graph, visited, first)
|
||||
|
||||
# 是否可到达第二边界
|
||||
visited = [[False] * M for _ in range(N)]
|
||||
for i in range(M):
|
||||
dfs(N - 1, i, graph, visited, second)
|
||||
for i in range(N):
|
||||
dfs(i, M - 1, graph, visited, second)
|
||||
|
||||
# 可到达第一边界和第二边界
|
||||
res = first & second
|
||||
|
||||
for x, y in res:
|
||||
print(f"{x} {y}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
@ -290,6 +418,350 @@ for (int j = 0; j < m; j++) {
|
||||
|
||||
### Javascript
|
||||
|
||||
#### 深搜
|
||||
|
||||
```javascript
|
||||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||||
// 创建readline接口
|
||||
let iter = r1[Symbol.asyncIterator]();
|
||||
// 创建异步迭代器
|
||||
const readline = async () => (await iter.next()).value;
|
||||
|
||||
let graph // 地图
|
||||
let N, M // 地图大小
|
||||
const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向
|
||||
|
||||
|
||||
// 读取输入,初始化地图
|
||||
const initGraph = async () => {
|
||||
let line = await readline();
|
||||
[N, M] = line.split(' ').map(Number);
|
||||
graph = new Array(N).fill(0).map(() => new Array(M).fill(0))
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
line = await readline()
|
||||
line = line.split(' ').map(Number)
|
||||
for (let j = 0; j < M; j++) {
|
||||
graph[i][j] = line[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 从(x,y)开始深度优先遍历地图
|
||||
* @param {*} graph 地图
|
||||
* @param {*} visited 可访问节点
|
||||
* @param {*} x 开始搜索节点的下标
|
||||
* @param {*} y 开始搜索节点的下标
|
||||
* @return {*}
|
||||
*/
|
||||
const dfs = (graph, visited, x, y) => {
|
||||
if (visited[x][y]) return
|
||||
visited[x][y] = true // 标记为可访问
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let nextx = x + dir[i][0]
|
||||
let nexty = y + dir[i][1]
|
||||
if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue //越界,跳过
|
||||
if (graph[x][y] < graph[nextx][nexty]) continue //不能流过.跳过
|
||||
dfs(graph, visited, nextx, nexty)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 判断地图上的(x, y)是否可以到达第一组边界和第二组边界
|
||||
* @param {*} x 坐标
|
||||
* @param {*} y 坐标
|
||||
* @return {*} true可以到达,false不可以到达
|
||||
*/
|
||||
const isResult = (x, y) => {
|
||||
let visited = new Array(N).fill(false).map(() => new Array(M).fill(false))
|
||||
|
||||
let isFirst = false //是否可到达第一边界
|
||||
let isSecond = false //是否可到达第二边界
|
||||
|
||||
// 深搜,将(x, y)可到达的所有节点做标记
|
||||
dfs(graph, visited, x, y)
|
||||
|
||||
// 判断能否到第一边界左边
|
||||
for (let i = 0; i < N; i++) {
|
||||
if (visited[i][0]) {
|
||||
isFirst = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 判断能否到第一边界上边
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (visited[0][j]) {
|
||||
isFirst = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 判断能否到第二边界右边
|
||||
for (let i = 0; i < N; i++) {
|
||||
if (visited[i][M - 1]) {
|
||||
isSecond = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 判断能否到第二边界下边
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (visited[N - 1][j]) {
|
||||
isSecond = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return isFirst && isSecond
|
||||
}
|
||||
|
||||
(async function () {
|
||||
|
||||
// 读取输入,初始化地图
|
||||
await initGraph()
|
||||
|
||||
// 遍历地图,判断是否能到达第一组边界和第二组边界
|
||||
for (let i = 0; i < N; i++) {
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (isResult(i, j)) console.log(i + ' ' + j);
|
||||
}
|
||||
}
|
||||
})()
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 广搜-解法一
|
||||
|
||||
```java
|
||||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||||
// 创建readline接口
|
||||
let iter = r1[Symbol.asyncIterator]();
|
||||
// 创建异步迭代器
|
||||
const readline = async () => (await iter.next()).value;
|
||||
|
||||
let graph // 地图
|
||||
let N, M // 地图大小
|
||||
const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向
|
||||
|
||||
|
||||
// 读取输入,初始化地图
|
||||
const initGraph = async () => {
|
||||
let line = await readline();
|
||||
[N, M] = line.split(' ').map(Number);
|
||||
graph = new Array(N).fill(0).map(() => new Array(M).fill(0))
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
line = await readline()
|
||||
line = line.split(' ').map(Number)
|
||||
for (let j = 0; j < M; j++) {
|
||||
graph[i][j] = line[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 从(x,y)开始广度优先遍历地图
|
||||
* @param {*} graph 地图
|
||||
* @param {*} visited 可访问节点
|
||||
* @param {*} x 开始搜索节点的下标
|
||||
* @param {*} y 开始搜索节点的下标
|
||||
* @return {*}
|
||||
*/
|
||||
const bfs = (graph, visited, x, y) => {
|
||||
let queue = []
|
||||
queue.push([x, y])
|
||||
visited[x][y] = true
|
||||
|
||||
while (queue.length) {
|
||||
const [xx, yy] = queue.shift()
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let nextx = xx + dir[i][0]
|
||||
let nexty = yy + dir[i][1]
|
||||
if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue //越界, 跳过
|
||||
|
||||
// 可访问或者不能流过, 跳过 (注意这里是graph[xx][yy] < graph[nextx][nexty], 不是graph[x][y] < graph[nextx][nexty])
|
||||
if (visited[nextx][nexty] || graph[xx][yy] < graph[nextx][nexty]) continue
|
||||
|
||||
queue.push([nextx, nexty])
|
||||
visited[nextx][nexty] = true
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 判断地图上的(x, y)是否可以到达第一组边界和第二组边界
|
||||
* @param {*} x 坐标
|
||||
* @param {*} y 坐标
|
||||
* @return {*} true可以到达,false不可以到达
|
||||
*/
|
||||
const isResult = (x, y) => {
|
||||
let visited = new Array(N).fill(false).map(() => new Array(M).fill(false))
|
||||
|
||||
let isFirst = false //是否可到达第一边界
|
||||
let isSecond = false //是否可到达第二边界
|
||||
|
||||
// 深搜,将(x, y)可到达的所有节点做标记
|
||||
bfs(graph, visited, x, y)
|
||||
|
||||
// console.log(visited);
|
||||
|
||||
// 判断能否到第一边界左边
|
||||
for (let i = 0; i < N; i++) {
|
||||
if (visited[i][0]) {
|
||||
isFirst = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 判断能否到第一边界上边
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (visited[0][j]) {
|
||||
isFirst = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 判断能否到第二边界右边
|
||||
for (let i = 0; i < N; i++) {
|
||||
if (visited[i][M - 1]) {
|
||||
isSecond = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 判断能否到第二边界下边
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (visited[N - 1][j]) {
|
||||
isSecond = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return isFirst && isSecond
|
||||
}
|
||||
|
||||
(async function () {
|
||||
|
||||
// 读取输入,初始化地图
|
||||
await initGraph()
|
||||
|
||||
// 遍历地图,判断是否能到达第一组边界和第二组边界
|
||||
for (let i = 0; i < N; i++) {
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (isResult(i, j)) console.log(i + ' ' + j);
|
||||
}
|
||||
}
|
||||
})()
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 广搜-解法二
|
||||
|
||||
从第一边界和第二边界开始向高处流, 标记可以流到的位置, 两个边界都能到达的位置就是所求结果
|
||||
|
||||
```javascript
|
||||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||||
// 创建readline接口
|
||||
let iter = r1[Symbol.asyncIterator]();
|
||||
// 创建异步迭代器
|
||||
const readline = async () => (await iter.next()).value;
|
||||
|
||||
let graph // 地图
|
||||
let N, M // 地图大小
|
||||
const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向
|
||||
|
||||
|
||||
// 读取输入,初始化地图
|
||||
const initGraph = async () => {
|
||||
let line = await readline();
|
||||
[N, M] = line.split(' ').map(Number);
|
||||
graph = new Array(N).fill(0).map(() => new Array(M).fill(0))
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
line = await readline()
|
||||
line = line.split(' ').map(Number)
|
||||
for (let j = 0; j < M; j++) {
|
||||
graph[i][j] = line[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 从(x,y)开始广度优先遍历地图
|
||||
* @param {*} graph 地图
|
||||
* @param {*} visited 可访问节点
|
||||
* @param {*} x 开始搜索节点的下标
|
||||
* @param {*} y 开始搜索节点的下标
|
||||
* @return {*}
|
||||
*/
|
||||
const bfs = (graph, visited, x, y) => {
|
||||
if(visited[x][y]) return
|
||||
|
||||
let queue = []
|
||||
queue.push([x, y])
|
||||
visited[x][y] = true
|
||||
|
||||
while (queue.length) {
|
||||
const [xx, yy] = queue.shift()
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let nextx = xx + dir[i][0]
|
||||
let nexty = yy + dir[i][1]
|
||||
if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue //越界, 跳过
|
||||
|
||||
// 可访问或者不能流过, 跳过 (注意因为是从边界往高处流, 所以这里是graph[xx][yy] >= graph[nextx][nexty], 还要注意不是graph[xx][yy] >= graph[nextx][nexty])
|
||||
if (visited[nextx][nexty] || graph[xx][yy] >= graph[nextx][nexty]) continue
|
||||
|
||||
queue.push([nextx, nexty])
|
||||
visited[nextx][nexty] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(async function () {
|
||||
|
||||
// 读取输入,初始化地图
|
||||
await initGraph()
|
||||
|
||||
// 记录第一边界可到达的节点
|
||||
let firstBorder = new Array(N).fill(false).map(() => new Array(M).fill(false))
|
||||
|
||||
// 记录第二边界可到达的节点
|
||||
let secondBorder = new Array(N).fill(false).map(() => new Array(M).fill(false))
|
||||
|
||||
// 第一边界左边和第二边界右边
|
||||
for (let i = 0; i < N; i++) {
|
||||
bfs(graph, firstBorder, i, 0)
|
||||
bfs(graph, secondBorder, i, M - 1)
|
||||
}
|
||||
|
||||
// 第一边界上边和第二边界下边
|
||||
for (let j = 0; j < M; j++) {
|
||||
bfs(graph, firstBorder, 0, j)
|
||||
bfs(graph, secondBorder, N - 1, j)
|
||||
}
|
||||
|
||||
// 遍历地图,判断是否能到达第一组边界和第二组边界
|
||||
for (let i = 0; i < N; i++) {
|
||||
for (let j = 0; j < M; j++) {
|
||||
if (firstBorder[i][j] && secondBorder[i][j]) console.log(i + ' ' + j);
|
||||
}
|
||||
}
|
||||
})()
|
||||
```
|
||||
|
||||
|
||||
|
||||
### TypeScript
|
||||
|
||||
### PhP
|
||||
|
@ -258,6 +258,111 @@ int main() {
|
||||
## 其他语言版本
|
||||
|
||||
### Java
|
||||
```Java
|
||||
public class Main {
|
||||
// 该方法采用 DFS
|
||||
// 定义全局变量
|
||||
// 记录每次每个岛屿的面积
|
||||
static int count;
|
||||
// 对每个岛屿进行标记
|
||||
static int mark;
|
||||
// 定义二维数组表示四个方位
|
||||
static int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
|
||||
|
||||
// DFS 进行搜索,将每个岛屿标记为不同的数字
|
||||
public static void dfs(int[][] grid, int x, int y, boolean[][] visited) {
|
||||
// 当遇到边界,直接return
|
||||
if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length) return;
|
||||
// 遇到已经访问过的或者遇到海水,直接返回
|
||||
if (visited[x][y] || grid[x][y] == 0) return;
|
||||
|
||||
visited[x][y] = true;
|
||||
count++;
|
||||
grid[x][y] = mark;
|
||||
|
||||
// 继续向下层搜索
|
||||
dfs(grid, x, y + 1, visited);
|
||||
dfs(grid, x, y - 1, visited);
|
||||
dfs(grid, x + 1, y, visited);
|
||||
dfs(grid, x - 1, y, visited);
|
||||
}
|
||||
|
||||
public static void main (String[] args) {
|
||||
// 接收输入
|
||||
Scanner sc = new Scanner(System.in);
|
||||
int m = sc.nextInt();
|
||||
int n = sc.nextInt();
|
||||
|
||||
int[][] grid = new int[m][n];
|
||||
for (int i = 0; i < m; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
grid[i][j] = sc.nextInt();
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化mark变量,从2开始(区别于0水,1岛屿)
|
||||
mark = 2;
|
||||
|
||||
// 定义二位boolean数组记录该位置是否被访问
|
||||
boolean[][] visited = new boolean[m][n];
|
||||
|
||||
// 定义一个HashMap,记录某片岛屿的标记号和面积
|
||||
HashMap<Integer, Integer> getSize = new HashMap<>();
|
||||
|
||||
// 定义一个HashSet,用来判断某一位置水四周是否存在不同标记编号的岛屿
|
||||
HashSet<Integer> set = new HashSet<>();
|
||||
|
||||
// 定义一个boolean变量,看看DFS之后,是否全是岛屿
|
||||
boolean isAllIsland = true;
|
||||
|
||||
// 遍历二维数组进行DFS搜索,标记每片岛屿的编号,记录对应的面积
|
||||
for (int i = 0; i < m; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
if (grid[i][j] == 0) isAllIsland = false;
|
||||
if (grid[i][j] == 1) {
|
||||
count = 0;
|
||||
dfs(grid, i, j, visited);
|
||||
getSize.put(mark, count);
|
||||
mark++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
if (isAllIsland) result = m * n;
|
||||
|
||||
// 对标记完的grid继续遍历,判断每个水位置四周是否有岛屿,并记录下四周不同相邻岛屿面积之和
|
||||
// 每次计算完一个水位置周围可能存在的岛屿面积之和,更新下result变量
|
||||
for (int i = 0; i < m; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
if (grid[i][j] == 0) {
|
||||
set.clear();
|
||||
// 当前水位置变更为岛屿,所以初始化为1
|
||||
int curSize = 1;
|
||||
|
||||
for (int[] dir : dirs) {
|
||||
int curRow = i + dir[0];
|
||||
int curCol = j + dir[1];
|
||||
|
||||
if (curRow < 0 || curRow >= m || curCol < 0 || curCol >= n) continue;
|
||||
int curMark = grid[curRow][curCol];
|
||||
// 如果当前相邻的岛屿已经遍历过或者HashMap中不存在这个编号,继续搜索
|
||||
if (set.contains(curMark) || !getSize.containsKey(curMark)) continue;
|
||||
set.add(curMark);
|
||||
curSize += getSize.get(curMark);
|
||||
}
|
||||
|
||||
result = Math.max(result, curSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 打印结果
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
|
@ -114,11 +114,11 @@ void dfs(const vector<list<int>>& graph, int key, vector<bool>& visited) {
|
||||
```C++
|
||||
// 写法二:处理下一个要访问的节点
|
||||
void dfs(const vector<list<int>>& graph, int key, vector<bool>& visited) {
|
||||
list<int> keys = rooms[key];
|
||||
list<int> keys = graph[key];
|
||||
for (int key : keys) {
|
||||
if (visited[key] == false) { // 确认下一个是没访问过的节点
|
||||
visited[key] = true;
|
||||
dfs(rooms, key, visited);
|
||||
dfs(graph, key, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -202,11 +202,11 @@ int main() {
|
||||
using namespace std;
|
||||
|
||||
void dfs(const vector<list<int>>& graph, int key, vector<bool>& visited) {
|
||||
list<int> keys = rooms[key];
|
||||
list<int> keys = graph[key];
|
||||
for (int key : keys) {
|
||||
if (visited[key] == false) { // 确认下一个是没访问过的节点
|
||||
visited[key] = true;
|
||||
dfs(rooms, key, visited);
|
||||
dfs(graph, key, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -223,7 +223,7 @@ int main() {
|
||||
}
|
||||
vector<bool> visited(n + 1, false);
|
||||
|
||||
visited[0] = true; // 节点1 预先处理
|
||||
visited[1] = true; // 节点1 预先处理
|
||||
dfs(graph, 1, visited);
|
||||
|
||||
for (int i = 1; i <= n; i++) {
|
||||
|
@ -66,7 +66,7 @@
|
||||
|
||||

|
||||
|
||||
该录友的下边空格出界了,则说明找到一条边。
|
||||
该陆地的下边空格出界了,则说明找到一条边。
|
||||
|
||||
|
||||
C++代码如下:(详细注释)
|
||||
@ -157,7 +157,62 @@ int main() {
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
### Java
|
||||
### Java
|
||||
```Java
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
// 每次遍历到1,探索其周围4个方向,并记录周长,最终合计
|
||||
// 声明全局变量,dirs表示4个方向
|
||||
static int[][] dirs = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
|
||||
// 统计每单个1的周长
|
||||
static int count;
|
||||
|
||||
// 探索其周围4个方向,并记录周长
|
||||
public static void helper(int[][] grid, int x, int y) {
|
||||
for (int[] dir : dirs) {
|
||||
int nx = x + dir[0];
|
||||
int ny = y + dir[1];
|
||||
|
||||
// 遇到边界或者水,周长加一
|
||||
if (nx < 0 || nx >= grid.length || ny < 0 || ny >= grid[0].length
|
||||
|| grid[nx][ny] == 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Scanner sc = new Scanner(System.in);
|
||||
|
||||
// 接收输入
|
||||
int M = sc.nextInt();
|
||||
int N = sc.nextInt();
|
||||
|
||||
int[][] grid = new int[M][N];
|
||||
for (int i = 0; i < M; i++) {
|
||||
for (int j = 0; j < N; j++) {
|
||||
grid[i][j] = sc.nextInt();
|
||||
}
|
||||
}
|
||||
|
||||
int result = 0; // 总周长
|
||||
for (int i = 0; i < M; i++) {
|
||||
for (int j = 0; j < N; j++) {
|
||||
if (grid[i][j] == 1) {
|
||||
count = 0;
|
||||
helper(grid, i, j);
|
||||
// 更新总周长
|
||||
result += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 打印结果
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
|
@ -158,7 +158,62 @@ int main() {
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
### Java
|
||||
### Java
|
||||
|
||||
```Java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main{
|
||||
public static void main(String[] args) {
|
||||
int N, M;
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
N = scanner.nextInt();
|
||||
M = scanner.nextInt();
|
||||
DisJoint disJoint = new DisJoint(N + 1);
|
||||
for (int i = 0; i < M; ++i) {
|
||||
disJoint.join(scanner.nextInt(), scanner.nextInt());
|
||||
}
|
||||
if(disJoint.isSame(scanner.nextInt(), scanner.nextInt())) {
|
||||
System.out.println("1");
|
||||
} else {
|
||||
System.out.println("0");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//并查集模板
|
||||
class DisJoint{
|
||||
private int[] father;
|
||||
|
||||
public DisJoint(int N) {
|
||||
father = new int[N];
|
||||
for (int i = 0; i < N; ++i){
|
||||
father[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
public int find(int n) {
|
||||
return n == father[n] ? n : (father[n] = find(father[n]));
|
||||
}
|
||||
|
||||
public void join (int n, int m) {
|
||||
n = find(n);
|
||||
m = find(m);
|
||||
if (n == m) return;
|
||||
father[m] = n;
|
||||
}
|
||||
|
||||
public boolean isSame(int n, int m){
|
||||
n = find(n);
|
||||
m = find(m);
|
||||
return n == m;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
|
@ -78,7 +78,7 @@
|
||||
|
||||

|
||||
|
||||
对于情况二,删掉构成环的边就可以了。
|
||||
对于情况三,删掉构成环的边就可以了。
|
||||
|
||||
|
||||
## 写代码
|
||||
@ -225,6 +225,7 @@ int main() {
|
||||
vec.push_back(i);
|
||||
}
|
||||
}
|
||||
// 情况一、情况二
|
||||
if (vec.size() > 0) {
|
||||
// 放在vec里的边已经按照倒叙放的,所以这里就优先删vec[0]这条边
|
||||
if (isTreeAfterRemoveEdge(edges, vec[0])) {
|
||||
|
@ -153,6 +153,68 @@ int main() {
|
||||
## 其他语言版本
|
||||
|
||||
### Java
|
||||
```Java
|
||||
public class Main {
|
||||
// BFS方法
|
||||
public static int ladderLength(String beginWord, String endWord, List<String> wordList) {
|
||||
// 使用set作为查询容器,效率更高
|
||||
HashSet<String> set = new HashSet<>(wordList);
|
||||
|
||||
// 声明一个queue存储每次变更一个字符得到的且存在于容器中的新字符串
|
||||
Queue<String> queue = new LinkedList<>();
|
||||
|
||||
// 声明一个hashMap存储遍历到的字符串以及所走过的路径path
|
||||
HashMap<String, Integer> visitMap = new HashMap<>();
|
||||
queue.offer(beginWord);
|
||||
visitMap.put(beginWord, 1);
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
String curWord = queue.poll();
|
||||
int path = visitMap.get(curWord);
|
||||
|
||||
for (int i = 0; i < curWord.length(); i++) {
|
||||
char[] ch = curWord.toCharArray();
|
||||
// 每个位置尝试26个字母
|
||||
for (char k = 'a'; k <= 'z'; k++) {
|
||||
ch[i] = k;
|
||||
|
||||
String newWord = new String(ch);
|
||||
if (newWord.equals(endWord)) return path + 1;
|
||||
|
||||
// 如果这个新字符串存在于容器且之前未被访问到
|
||||
if (set.contains(newWord) && !visitMap.containsKey(newWord)) {
|
||||
visitMap.put(newWord, path + 1);
|
||||
queue.offer(newWord);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void main (String[] args) {
|
||||
/* code */
|
||||
// 接收输入
|
||||
Scanner sc = new Scanner(System.in);
|
||||
int N = sc.nextInt();
|
||||
sc.nextLine();
|
||||
String[] strs = sc.nextLine().split(" ");
|
||||
|
||||
List<String> wordList = new ArrayList<>();
|
||||
for (int i = 0; i < N; i++) {
|
||||
wordList.add(sc.nextLine());
|
||||
}
|
||||
|
||||
// wordList.add(strs[1]);
|
||||
|
||||
// 打印结果
|
||||
int result = ladderLength(strs[0], strs[1], wordList);
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
|
59
problems/kamacoder/0113.国际象棋.md
Normal file
59
problems/kamacoder/0113.国际象棋.md
Normal file
@ -0,0 +1,59 @@
|
||||
|
||||
# 113.国际象棋
|
||||
|
||||
广搜,但本题如果广搜枚举马和象的话会超时。
|
||||
|
||||
广搜要只枚举马的走位,同时判断是否在对角巷直接走象
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
const int N = 100005, mod = 1000000007;
|
||||
using ll = long long;
|
||||
int n, ans;
|
||||
int dir[][2] = {{1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, -1}, {-2, 1}};
|
||||
int main() {
|
||||
int x1, y1, x2, y2;
|
||||
cin >> n;
|
||||
while (n--) {
|
||||
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
|
||||
if (x1 == x2 && y1 == y2) {
|
||||
cout << 0 << endl;
|
||||
continue;
|
||||
}
|
||||
// 判断象走一步到达
|
||||
int d = abs(x1 - x2) - abs(y1 - y2);
|
||||
if (!d) {cout << 1 << endl; continue;}
|
||||
// 判断马走一步到达
|
||||
bool one = 0;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
int dx = x1 + dir[i][0], dy = y1 + dir[i][1];
|
||||
if (dx == x2 && dy == y2) {
|
||||
cout << 1 << endl;
|
||||
one = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (one) continue;
|
||||
// 接下来为两步的逻辑, 象走两步或者马走一步,象走一步
|
||||
// 象直接两步可以到达,这个计算是不是同颜色的格子,象可以在两步到达所有同颜色的格子
|
||||
int d2 = abs(x1 - x2) + abs(y1 - y2);
|
||||
if (d2 % 2 == 0) {
|
||||
cout << 2 << endl;
|
||||
continue;
|
||||
}
|
||||
// 接下来判断马 + 象的组合
|
||||
bool two = 0;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
int dx = x1 + dir[i][0], dy = y1 + dir[i][1];
|
||||
int d = abs(dx - x2) - abs(dy - y2);
|
||||
if (!d) {cout << 2 << endl; two = true; break;}
|
||||
}
|
||||
if (two) continue;
|
||||
// 剩下的格子全都是三步到达的
|
||||
cout << 3 << endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
132
problems/kamacoder/0121.小红的区间翻转.md
Normal file
132
problems/kamacoder/0121.小红的区间翻转.md
Normal file
@ -0,0 +1,132 @@
|
||||
|
||||
# 121. 小红的区间翻转
|
||||
|
||||
比较暴力的方式,就是直接模拟, 枚举所有 区间,然后检查其翻转的情况。
|
||||
|
||||
在检查翻转的时候,需要一些代码优化,否则容易超时。
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
bool canTransform(const vector<int>& a, const vector<int>& b, int left, int right) {
|
||||
// 提前检查翻转区间的值是否可以匹配
|
||||
for (int i = left, j = right; i <= right; i++, j--) {
|
||||
if (a[i] != b[j]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 检查翻转区间外的值是否匹配
|
||||
for (int i = 0; i < left; i++) {
|
||||
if (a[i] != b[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = right + 1; i < a.size(); i++) {
|
||||
if (a[i] != b[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int n;
|
||||
cin >> n;
|
||||
|
||||
vector<int> a(n);
|
||||
vector<int> b(n);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
cin >> a[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
cin >> b[i];
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
// 遍历所有可能的区间
|
||||
for (int left = 0; left < n; left++) {
|
||||
for (int right = left; right < n; right++) {
|
||||
// 检查翻转区间 [left, right] 后,a 是否可以变成 b
|
||||
if (canTransform(a, b, left, right)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
cout << count << endl;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
也可以事先计算好,最长公共前缀,和最长公共后缀。
|
||||
|
||||
在公共前缀和公共后缀之间的部分进行翻转操作,这样我们可以减少很多不必要的翻转尝试。
|
||||
|
||||
通过在公共前缀和后缀之间的部分,找到可以通过翻转使得 a 和 b 相等的区间。
|
||||
|
||||
以下 为评论区 卡码网用户:码鬼的C++代码
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
int n;
|
||||
cin >> n;
|
||||
vector<int> a(n), b(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
cin >> a[i];
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
cin >> b[i];
|
||||
}
|
||||
|
||||
vector<int> prefix(n, 0), suffix(n, 0);
|
||||
|
||||
// 计算前缀相等的位置
|
||||
int p = 0;
|
||||
while (p < n && a[p] == b[p]) {
|
||||
prefix[p] = 1;
|
||||
p++;
|
||||
}
|
||||
|
||||
// 计算后缀相等的位置
|
||||
int s = n - 1;
|
||||
while (s >= 0 && a[s] == b[s]) {
|
||||
suffix[s] = 1;
|
||||
s--;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
// 遍历所有可能的区间
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
for (int j = i + 1; j < n; j++) {
|
||||
// 判断前缀和后缀是否相等
|
||||
if ((i == 0 || prefix[i - 1] == 1) && (j == n - 1 || suffix[j + 1] == 1)) {
|
||||
// 判断翻转后的子数组是否和目标数组相同
|
||||
bool is_palindrome = true;
|
||||
for (int k = 0; k <= (j - i) / 2; k++) {
|
||||
if (a[i + k] != b[j - k]) {
|
||||
is_palindrome = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_palindrome) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cout << count << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
147
problems/kamacoder/0135.获取连通的相邻节点列表.md
Normal file
147
problems/kamacoder/0135.获取连通的相邻节点列表.md
Normal file
@ -0,0 +1,147 @@
|
||||
|
||||
# 135. 获取连通的相邻节点列表
|
||||
|
||||
本题是一个 “阅读理解”题,其实题目的算法很简单,但理解题意很费劲。
|
||||
|
||||
题目描述中的【提示信息】 是我后加上去了,华为笔试的时候没有这个 【提示信息】。
|
||||
|
||||
相信没有 【提示信息】大家理解题意 平均要多用半个小时。
|
||||
|
||||
思路:
|
||||
|
||||
1. 将第一行数据加入set中
|
||||
2. 后面输出数据,判断是否在 set里
|
||||
3. 最后把结果排个序
|
||||
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
using namespace std;
|
||||
int main() {
|
||||
unordered_set<int> uset;
|
||||
int n, a;
|
||||
cin >> n;
|
||||
while (n--) {
|
||||
cin >> a;
|
||||
uset.insert(a);
|
||||
}
|
||||
int m, x, vlan_id;
|
||||
long long tb;
|
||||
vector<long long> vecTB;
|
||||
cin >> m;
|
||||
while(m--) {
|
||||
cin >> tb;
|
||||
cin >> x;
|
||||
vector<long long> vecVlan_id(x);
|
||||
for (int i = 0; i < x; i++) {
|
||||
cin >> vecVlan_id[i];
|
||||
}
|
||||
for (int i = 0; i < x; i++) {
|
||||
if (uset.find(vecVlan_id[i]) != uset.end()) {
|
||||
vecTB.push_back(tb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
cout << vecTB.size() << endl;
|
||||
if (vecTB.size() != 0) {
|
||||
sort(vecTB.begin(), vecTB.end());
|
||||
for (int i = 0; i < vecTB.size() ; i++) cout << vecTB[i] << " ";
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
### Java
|
||||
|
||||
```Java
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
Set<Integer> uset = new HashSet<>();
|
||||
int n = scanner.nextInt();
|
||||
while (n-- > 0) {
|
||||
int a = scanner.nextInt();
|
||||
uset.add(a);
|
||||
}
|
||||
|
||||
int m = scanner.nextInt();
|
||||
List<Long> vecTB = new ArrayList<>();
|
||||
while (m-- > 0) {
|
||||
long tb = scanner.nextLong();
|
||||
int x = scanner.nextInt();
|
||||
List<Integer> vecVlan_id = new ArrayList<>();
|
||||
for (int i = 0; i < x; i++) {
|
||||
vecVlan_id.add(scanner.nextInt());
|
||||
}
|
||||
for (int vlanId : vecVlan_id) {
|
||||
if (uset.contains(vlanId)) {
|
||||
vecTB.add(tb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(vecTB.size());
|
||||
if (!vecTB.isEmpty()) {
|
||||
Collections.sort(vecTB);
|
||||
for (long tb : vecTB) {
|
||||
System.out.print(tb + " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
def main():
|
||||
import sys
|
||||
input = sys.stdin.read
|
||||
data = input().split()
|
||||
|
||||
index = 0
|
||||
n = int(data[index])
|
||||
index += 1
|
||||
uset = set()
|
||||
for _ in range(n):
|
||||
a = int(data[index])
|
||||
index += 1
|
||||
uset.add(a)
|
||||
|
||||
m = int(data[index])
|
||||
index += 1
|
||||
vecTB = []
|
||||
while m > 0:
|
||||
tb = int(data[index])
|
||||
index += 1
|
||||
x = int(data[index])
|
||||
index += 1
|
||||
vecVlan_id = []
|
||||
for _ in range(x):
|
||||
vecVlan_id.append(int(data[index]))
|
||||
index += 1
|
||||
for vlan_id in vecVlan_id:
|
||||
if vlan_id in uset:
|
||||
vecTB.append(tb)
|
||||
break
|
||||
m -= 1
|
||||
|
||||
print(len(vecTB))
|
||||
if vecTB:
|
||||
vecTB.sort()
|
||||
print(" ".join(map(str, vecTB)))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
148
problems/kamacoder/0136.字符串处理器.md
Normal file
148
problems/kamacoder/0136.字符串处理器.md
Normal file
@ -0,0 +1,148 @@
|
||||
|
||||
# 字符串处理器
|
||||
|
||||
纯模拟,但情况比较多,非常容易 空指针异常。
|
||||
|
||||
大家要注意,边界问题 以及 负数问题。
|
||||
|
||||
整体代码如下:
|
||||
|
||||
```CPP
|
||||
#include<bits/stdc++.h>
|
||||
using namespace std;
|
||||
int main() {
|
||||
int index = 0;
|
||||
long long optNum;
|
||||
string s;
|
||||
string cmd;
|
||||
while(cin >> cmd){
|
||||
//cout << s << endl;
|
||||
if(cmd == "insert") {
|
||||
string buff;
|
||||
cin >> buff;
|
||||
s.insert(index, buff);
|
||||
index += buff.size();
|
||||
}
|
||||
else if(cmd == "move") {
|
||||
cin >> optNum;
|
||||
if(optNum > 0 && index + optNum <= s.size()) index += optNum;
|
||||
if(optNum < 0 && index >= -optNum) index += optNum;
|
||||
}
|
||||
else if(cmd == "delete") {
|
||||
cin >> optNum;
|
||||
if(index >= optNum && optNum > 0){
|
||||
s.erase(index - optNum, optNum);
|
||||
index -= optNum;
|
||||
}
|
||||
}
|
||||
else if(cmd == "copy") {
|
||||
if(index > 0) {
|
||||
string tmp = s.substr(0, index);
|
||||
s.insert(index, tmp);
|
||||
}
|
||||
}
|
||||
else if(cmd == "end") {
|
||||
for(int i = 0; i < index; i++) {
|
||||
cout << s[i];
|
||||
}
|
||||
cout << '|';
|
||||
for(int i = index; i < s.size(); i++) cout << s[i];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
### Java
|
||||
|
||||
```Java
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
StringBuilder s = new StringBuilder();
|
||||
int index = 0;
|
||||
int optNum;
|
||||
|
||||
while (true) {
|
||||
String cmd = scanner.next();
|
||||
if (cmd.equals("insert")) {
|
||||
String buff = scanner.next();
|
||||
s.insert(index, buff);
|
||||
index += buff.length();
|
||||
} else if (cmd.equals("move")) {
|
||||
optNum = scanner.nextInt();
|
||||
if (optNum > 0 && index + optNum <= s.length()) index += optNum;
|
||||
if (optNum < 0 && index >= -optNum) index += optNum;
|
||||
} else if (cmd.equals("delete")) {
|
||||
optNum = scanner.nextInt();
|
||||
if (index >= optNum && optNum > 0) {
|
||||
s.delete(index - optNum, index);
|
||||
index -= optNum;
|
||||
}
|
||||
} else if (cmd.equals("copy")) {
|
||||
if (index > 0) {
|
||||
String tmp = s.substring(0, index);
|
||||
s.insert(index, tmp);
|
||||
}
|
||||
} else if (cmd.equals("end")) {
|
||||
System.out.print(s.substring(0, index) + '|' + s.substring(index));
|
||||
break;
|
||||
}
|
||||
}
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
def main():
|
||||
import sys
|
||||
input = sys.stdin.read
|
||||
data = input().split()
|
||||
s = ""
|
||||
index = 0
|
||||
i = 0
|
||||
|
||||
while i < len(data):
|
||||
cmd = data[i]
|
||||
i += 1
|
||||
if cmd == "insert":
|
||||
buff = data[i]
|
||||
i += 1
|
||||
s = s[:index] + buff + s[index:]
|
||||
index += len(buff)
|
||||
elif cmd == "move":
|
||||
optNum = int(data[i])
|
||||
i += 1
|
||||
if optNum > 0 and index + optNum <= len(s):
|
||||
index += optNum
|
||||
elif optNum < 0 and index >= -optNum:
|
||||
index += optNum
|
||||
elif cmd == "delete":
|
||||
optNum = int(data[i])
|
||||
i += 1
|
||||
if index >= optNum and optNum > 0:
|
||||
s = s[:index - optNum] + s[index:]
|
||||
index -= optNum
|
||||
elif cmd == "copy":
|
||||
if index > 0:
|
||||
tmp = s[:index]
|
||||
s = s[:index] + tmp + s[index:]
|
||||
elif cmd == "end":
|
||||
print(s[:index] + '|' + s[index:])
|
||||
break
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
```
|
192
problems/kamacoder/0137.消息传输.md
Normal file
192
problems/kamacoder/0137.消息传输.md
Normal file
@ -0,0 +1,192 @@
|
||||
|
||||
# 137. 消息传输
|
||||
|
||||
这道题目,普通广搜就可以解决。
|
||||
|
||||
这里说一下几点注意事项:
|
||||
|
||||
1、 题目描述中,注意 n 是列数,m是行数
|
||||
|
||||
这是造成很多录友周赛的时候提交 返回 【运行错误】的罪魁祸首,如果 输入用例是 正方形,那没问题,如果后台输入用例是矩形, n 和 m 搞反了,就会数组越界。
|
||||
|
||||
矩阵是 m * n ,但输入的顺序却是 先输入n 再输入 m。
|
||||
|
||||
这会让很多人把矩阵的 n 和 m 搞反。
|
||||
|
||||
其实规范出题,就应该是n 行,m列,然后 先输入n,在输入m。
|
||||
|
||||
只能说 大厂出题的人,也不是专业出题的,所以会在 非算法方面一不小心留下很多 “bug”,消耗大家的精力。
|
||||
|
||||
2、再写广搜的时候,可能担心会无限循环
|
||||
|
||||
即 A 走到 B,B又走到A,A又走到B ,这种情况,一般来说 广搜都是用一个 visit数组来标记的。
|
||||
|
||||
但本题不用,因为 不会重复走的,题图里的信号都是正数,根据距离判断大小 可以保证不走回头路。
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
const int inf = 1e6;
|
||||
int main () {
|
||||
int n, m, startx, starty;
|
||||
cin >> n >> m;
|
||||
cin >> startx >> starty;
|
||||
vector<vector<int>> grid(m, vector<int>(n));
|
||||
vector<vector<int>> dis(m, vector<int>(n, inf));
|
||||
for (int i = 0; i < m; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
cin >> grid[i][j];
|
||||
}
|
||||
}
|
||||
queue<pair<int, int>> que;
|
||||
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
|
||||
que.push(pair<int, int>(startx, starty));
|
||||
dis[startx][starty] = 0;
|
||||
while(!que.empty()) {
|
||||
pair<int, int> cur = que.front(); que.pop();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int newx = cur.first + dir[i][1];
|
||||
int newy = cur.second + dir[i][0];
|
||||
if (newx < 0 || newx >= m || newy < 0 || newy >= n || grid[cur.first][cur.second] == 0) continue;
|
||||
|
||||
if (dis[newx][newy] > dis[cur.first][cur.second] + grid[cur.first][cur.second]) {
|
||||
dis[newx][newy] = dis[cur.first][cur.second] + grid[cur.first][cur.second];
|
||||
que.push(pair<int, int>(newx, newy));
|
||||
}
|
||||
}
|
||||
}
|
||||
int result = 0;
|
||||
for (int i = 0; i < m; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
if (dis[i][j] == inf) {
|
||||
cout << -1 << endl;
|
||||
return 0;
|
||||
}
|
||||
result = max(result, dis[i][j]);
|
||||
}
|
||||
}
|
||||
cout << result << endl;
|
||||
}
|
||||
```
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
### Java
|
||||
|
||||
```Java
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
static final int INF = 1000000;
|
||||
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int n = scanner.nextInt();
|
||||
int m = scanner.nextInt();
|
||||
int startX = scanner.nextInt();
|
||||
int startY = scanner.nextInt();
|
||||
|
||||
int[][] grid = new int[m][n];
|
||||
int[][] dis = new int[m][n];
|
||||
|
||||
for (int i = 0; i < m; i++) {
|
||||
Arrays.fill(dis[i], INF);
|
||||
for (int j = 0; j < n; j++) {
|
||||
grid[i][j] = scanner.nextInt();
|
||||
}
|
||||
}
|
||||
|
||||
Queue<int[]> queue = new LinkedList<>();
|
||||
int[][] directions = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
|
||||
|
||||
queue.add(new int[]{startX, startY});
|
||||
dis[startX][startY] = 0;
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
int[] current = queue.poll();
|
||||
for (int[] dir : directions) {
|
||||
int newX = current[0] + dir[0];
|
||||
int newY = current[1] + dir[1];
|
||||
if (newX >= 0 && newX < m && newY >= 0 && newY < n && grid[current[0]][current[1]] != 0) {
|
||||
if (dis[newX][newY] > dis[current[0]][current[1]] + grid[current[0]][current[1]]) {
|
||||
dis[newX][newY] = dis[current[0]][current[1]] + grid[current[0]][current[1]];
|
||||
queue.add(new int[]{newX, newY});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
for (int i = 0; i < m; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
if (dis[i][j] == INF) {
|
||||
System.out.println(-1);
|
||||
return;
|
||||
}
|
||||
result = Math.max(result, dis[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(result);
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```Python
|
||||
from collections import deque
|
||||
|
||||
inf = 1000000
|
||||
|
||||
def main():
|
||||
import sys
|
||||
input = sys.stdin.read
|
||||
data = input().split()
|
||||
index = 0
|
||||
|
||||
n = int(data[index])
|
||||
m = int(data[index+1])
|
||||
startx = int(data[index+2])
|
||||
starty = int(data[index+3])
|
||||
index += 4
|
||||
|
||||
grid = []
|
||||
dis = [[inf] * n for _ in range(m)]
|
||||
|
||||
for i in range(m):
|
||||
grid.append([int(data[index+j]) for j in range(n)])
|
||||
index += n
|
||||
|
||||
directions = [(0, 1), (1, 0), (-1, 0), (0, -1)]
|
||||
queue = deque()
|
||||
queue.append((startx, starty))
|
||||
dis[startx][starty] = 0
|
||||
|
||||
while queue:
|
||||
curx, cury = queue.popleft()
|
||||
for dx, dy in directions:
|
||||
newx, newy = curx + dx, cury + dy
|
||||
if 0 <= newx < m and 0 <= newy < n and grid[curx][cury] != 0:
|
||||
if dis[newx][newy] > dis[curx][cury] + grid[curx][cury]:
|
||||
dis[newx][newy] = dis[curx][cury] + grid[curx][cury]
|
||||
queue.append((newx, newy))
|
||||
|
||||
result = 0
|
||||
for i in range(m):
|
||||
for j in range(n):
|
||||
if dis[i][j] == inf:
|
||||
print(-1)
|
||||
return
|
||||
result = max(result, dis[i][j])
|
||||
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
```
|
101
problems/kamacoder/0139.可爱串.md
Normal file
101
problems/kamacoder/0139.可爱串.md
Normal file
@ -0,0 +1,101 @@
|
||||
|
||||
# 可爱串
|
||||
|
||||
整体思路,就含有 子序列的字符串数量 减去 含有子串的字符串数量。
|
||||
|
||||
因为子序列数量已经是包含子串数量的。 剩下的就是 只有子序列 且没有子串的 字符串数量。
|
||||
|
||||
|
||||
需要注意我们求的不是 长度为 i 的字符串里有多少个 red 子序列。
|
||||
|
||||
**而是 可以有多少个 长度为i 的字符串 含有子序列 red**
|
||||
|
||||
同理,可以有多少个长度为i的字符串含有 red 子串
|
||||
|
||||
认清这一点很重要!
|
||||
|
||||
### 求子串
|
||||
|
||||
dp2[i][3] 长度为i 且 含有子串 red 的字符串数量 有多少
|
||||
|
||||
dp2[i][2] 长度为i 且 含有子串 re 的字符串数量有多少
|
||||
|
||||
dp2[i][1] 长度为 i 且 含有子串 r 的字符串数量有多少
|
||||
|
||||
dp2[1][0] 长度为 i 且 含有 只有 de, ee , e, d的字符串的字符串数量有多少。
|
||||
|
||||
```CPP
|
||||
// 求子串
|
||||
dp2[0][0] = 1;
|
||||
for(int i = 1;i <= n; i++) {
|
||||
dp2[i][0] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0] * 2) % mod; // 含有 re 的可以把 r改成d, 含有r 的可以改成
|
||||
dp2[i][1] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0]) % mod;
|
||||
dp2[i][2] = (dp2[i - 1][1]);
|
||||
dp2[i][3] = (dp2[i - 1][3] * 3 + dp2[i - 1][2]) % mod;
|
||||
}
|
||||
``
|
||||
|
||||
### 求子序列
|
||||
|
||||
dp1[i][3] 长度为i 且 含有子序列 red 的字符串数量 有多少
|
||||
|
||||
dp2[i][2] 长度为i 且 含有子序列 re 的字符串数量有多少
|
||||
|
||||
dp2[i][1] 长度为 i 且 含有子序列 r 的字符串数量有多少
|
||||
|
||||
dp2[1][0] 长度为 i 且 含有 只含有 e 和 d 的字符串的字符串数量有多少。
|
||||
|
||||
```CPP
|
||||
|
||||
// 求子序列
|
||||
dp1[0][0]=1;
|
||||
for(int i=1;i<=n;i++)
|
||||
{
|
||||
dp1[i][0] = (dp1[i - 1][0] * 2) % mod;
|
||||
dp1[i][1] = (dp1[i - 1][0] + dp1[i - 1][1] * 2) % mod;
|
||||
dp1[i][2] = (dp1[i - 1][1] + dp1[i - 1][2] * 2) % mod;
|
||||
dp1[i][3] = (dp1[i - 1][2] + dp1[i - 1][3] * 3) % mod;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
```CPP
|
||||
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
using ll=long long;
|
||||
const int mod=1e9+7;
|
||||
|
||||
int main()
|
||||
{
|
||||
int n;
|
||||
|
||||
cin>>n;
|
||||
vector<vector<ll>> dp1(n + 1,vector<ll> (4,0));
|
||||
vector<vector<ll>> dp2(n + 1,vector<ll> (4,0));
|
||||
// 求子串
|
||||
dp2[0][0] = 1;
|
||||
for(int i = 1;i <= n; i++) {
|
||||
dp2[i][0] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0] * 2) % mod;
|
||||
dp2[i][1] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0]) % mod;
|
||||
dp2[i][2] = (dp2[i - 1][1]);
|
||||
dp2[i][3] = (dp2[i - 1][3] * 3 + dp2[i - 1][2]) % mod;
|
||||
}
|
||||
|
||||
// 求子序列
|
||||
dp1[0][0]=1;
|
||||
for(int i=1;i<=n;i++)
|
||||
{
|
||||
dp1[i][0] = (dp1[i - 1][0] * 2) % mod;
|
||||
dp1[i][1] = (dp1[i - 1][0] + dp1[i - 1][1] * 2) % mod;
|
||||
dp1[i][2] = (dp1[i - 1][1] + dp1[i - 1][2] * 2) % mod;
|
||||
dp1[i][3] = (dp1[i - 1][2] + dp1[i - 1][3] * 3) % mod;
|
||||
}
|
||||
|
||||
cout<<(dp1[n][3] - dp2[n][3])%mod;
|
||||
|
||||
}
|
||||
|
||||
```
|
108
problems/kamacoder/0142.两个字符串的最小ASCII删除总和.md
Normal file
108
problems/kamacoder/0142.两个字符串的最小ASCII删除总和.md
Normal file
@ -0,0 +1,108 @@
|
||||
|
||||
# 142. 两个字符串的最小 ASCII 删除总和
|
||||
|
||||
本题和[代码随想录:两个字符串的删除操作](https://www.programmercarl.com/0583.%E4%B8%A4%E4%B8%AA%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E5%88%A0%E9%99%A4%E6%93%8D%E4%BD%9C.html) 思路基本是一样的。
|
||||
|
||||
属于编辑距离问题,如果想彻底了解,建议看看「代码随想录」的编辑距离总结篇。
|
||||
|
||||
本题dp数组含义:
|
||||
|
||||
dp[i][j] 表示 以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最小ASCII 删除总和。
|
||||
|
||||
如果 s1[i - 1] 与 s2[j - 1] 相同,则不用删:`dp[i][j] = dp[i - 1][j - 1]`
|
||||
|
||||
如果 s1[i - 1] 与 s2[j - 1] 不相同,删word1 的 最小删除和: `dp[i - 1][j] + s1[i - 1]` ,删word2的最小删除和: `dp[i][j - 1] + s2[j - 1]`
|
||||
|
||||
取最小值: `dp[i][j] = min(dp[i - 1][j] + s1[i - 1], dp[i][j - 1] + s2[j - 1])`
|
||||
|
||||
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
int main() {
|
||||
string s1, s2;
|
||||
cin >> s1 >> s2;
|
||||
vector<vector<int>> dp(s1.size() + 1, vector<int>(s2.size() + 1, 0));
|
||||
|
||||
// s1 如果变成空串的最小删除ASCLL值综合
|
||||
for (int i = 1; i <= s1.size(); i++) dp[i][0] = dp[i - 1][0] + s1[i - 1];
|
||||
// s2 如果变成空串的最小删除ASCLL值综合
|
||||
for (int j = 1; j <= s2.size(); j++) dp[0][j] = dp[0][j - 1] + s2[j - 1];
|
||||
|
||||
for (int i = 1; i <= s1.size(); i++) {
|
||||
for (int j = 1; j <= s2.size(); j++) {
|
||||
if (s1[i - 1] == s2[j - 1]) dp[i][j] = dp[i - 1][j - 1];
|
||||
else dp[i][j] = min(dp[i - 1][j] + s1[i - 1], dp[i][j - 1] + s2[j - 1]);
|
||||
}
|
||||
}
|
||||
cout << dp[s1.size()][s2.size()] << endl;
|
||||
}
|
||||
```
|
||||
|
||||
### Java
|
||||
|
||||
```Java
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
String s1 = scanner.nextLine();
|
||||
String s2 = scanner.nextLine();
|
||||
int[][] dp = new int[s1.length() + 1][s2.length() + 1];
|
||||
|
||||
// s1 如果变成空串的最小删除ASCII值综合
|
||||
for (int i = 1; i <= s1.length(); i++) {
|
||||
dp[i][0] = dp[i - 1][0] + s1.charAt(i - 1);
|
||||
}
|
||||
// s2 如果变成空串的最小删除ASCII值综合
|
||||
for (int j = 1; j <= s2.length(); j++) {
|
||||
dp[0][j] = dp[0][j - 1] + s2.charAt(j - 1);
|
||||
}
|
||||
|
||||
for (int i = 1; i <= s1.length(); i++) {
|
||||
for (int j = 1; j <= s2.length(); j++) {
|
||||
if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
dp[i][j] = Math.min(dp[i - 1][j] + s1.charAt(i - 1), dp[i][j - 1] + s2.charAt(j - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(dp[s1.length()][s2.length()]);
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
### python
|
||||
|
||||
```python
|
||||
def min_delete_sum(s1: str, s2: str) -> int:
|
||||
dp = [[0] * (len(s2) + 1) for _ in range(len(s1) + 1)]
|
||||
|
||||
# s1 如果变成空串的最小删除ASCII值综合
|
||||
for i in range(1, len(s1) + 1):
|
||||
dp[i][0] = dp[i - 1][0] + ord(s1[i - 1])
|
||||
# s2 如果变成空串的最小删除ASCII值综合
|
||||
for j in range(1, len(s2) + 1):
|
||||
dp[0][j] = dp[0][j - 1] + ord(s2[j - 1])
|
||||
|
||||
for i in range(1, len(s1) + 1):
|
||||
for j in range(1, len(s2) + 1):
|
||||
if s1[i - 1] == s2[j - 1]:
|
||||
dp[i][j] = dp[i - 1][j - 1]
|
||||
else:
|
||||
dp[i][j] = min(dp[i - 1][j] + ord(s1[i - 1]), dp[i][j - 1] + ord(s2[j - 1]))
|
||||
|
||||
return dp[len(s1)][len(s2)]
|
||||
|
||||
if __name__ == "__main__":
|
||||
s1 = input().strip()
|
||||
s2 = input().strip()
|
||||
print(min_delete_sum(s1, s2))
|
||||
```
|
237
problems/kamacoder/0143.最长同值路径.md
Normal file
237
problems/kamacoder/0143.最长同值路径.md
Normal file
@ -0,0 +1,237 @@
|
||||
|
||||
|
||||
# 143. 最长同值路径
|
||||
|
||||
|
||||
本题两个考点:
|
||||
|
||||
1. 层序遍历构造二叉树
|
||||
2. 树形dp,找出最长路径
|
||||
|
||||
对于写代码不多,或者动手能力比较差的录友,第一个 构造二叉树 基本就被卡主了。
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// 定义二叉树节点结构
|
||||
struct TreeNode {
|
||||
int val;
|
||||
TreeNode* left;
|
||||
TreeNode* right;
|
||||
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
|
||||
};
|
||||
|
||||
// 根据层序遍历数组构建二叉树
|
||||
TreeNode* constructBinaryTree(const vector<string>& levelOrder) {
|
||||
if (levelOrder.empty()) return NULL;
|
||||
|
||||
TreeNode* root = new TreeNode(stoi(levelOrder[0]));
|
||||
queue<TreeNode*> q;
|
||||
q.push(root);
|
||||
int i = 1;
|
||||
|
||||
while (!q.empty() && i < levelOrder.size()) {
|
||||
TreeNode* current = q.front();
|
||||
q.pop();
|
||||
|
||||
if (i < levelOrder.size() && levelOrder[i] != "null") {
|
||||
current->left = new TreeNode(stoi(levelOrder[i]));
|
||||
q.push(current->left);
|
||||
}
|
||||
i++;
|
||||
|
||||
if (i < levelOrder.size() && levelOrder[i] != "null") {
|
||||
current->right = new TreeNode(stoi(levelOrder[i]));
|
||||
q.push(current->right);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
|
||||
// 树形DP
|
||||
int dfs(TreeNode* node) {
|
||||
if (node == NULL) return 0;
|
||||
int leftPath = dfs(node->left);
|
||||
int rightPath = dfs(node->right);
|
||||
|
||||
int leftNum = 0, rightNum = 0;
|
||||
if (node->left != NULL && node->left->val == node->val) {
|
||||
leftNum = leftPath + 1;
|
||||
}
|
||||
if (node->right != NULL && node->right->val == node->val) {
|
||||
rightNum = rightPath + 1;
|
||||
}
|
||||
result = max(result, leftNum + rightNum);
|
||||
return max(leftNum, rightNum);
|
||||
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
int n;
|
||||
cin >> n;
|
||||
vector<string> levelOrder(n);
|
||||
for (int i = 0; i < n ; i++) cin >> levelOrder[i];
|
||||
|
||||
TreeNode* root = constructBinaryTree(levelOrder);
|
||||
dfs(root);
|
||||
cout << result << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Java
|
||||
|
||||
```Java
|
||||
import java.util.*;
|
||||
|
||||
class TreeNode {
|
||||
int val;
|
||||
TreeNode left, right;
|
||||
TreeNode(int x) {
|
||||
val = x;
|
||||
left = null;
|
||||
right = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public static int result = 0;
|
||||
|
||||
public static TreeNode constructBinaryTree(List<String> levelOrder) {
|
||||
if (levelOrder.isEmpty()) return null;
|
||||
|
||||
TreeNode root = new TreeNode(Integer.parseInt(levelOrder.get(0)));
|
||||
Queue<TreeNode> queue = new LinkedList<>();
|
||||
queue.add(root);
|
||||
int i = 1;
|
||||
|
||||
while (!queue.isEmpty() && i < levelOrder.size()) {
|
||||
TreeNode current = queue.poll();
|
||||
|
||||
if (i < levelOrder.size() && !levelOrder.get(i).equals("null")) {
|
||||
current.left = new TreeNode(Integer.parseInt(levelOrder.get(i)));
|
||||
queue.add(current.left);
|
||||
}
|
||||
i++;
|
||||
|
||||
if (i < levelOrder.size() && !levelOrder.get(i).equals("null")) {
|
||||
current.right = new TreeNode(Integer.parseInt(levelOrder.get(i)));
|
||||
queue.add(current.right);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
public static int dfs(TreeNode node) {
|
||||
if (node == null) return 0;
|
||||
int leftPath = dfs(node.left);
|
||||
int rightPath = dfs(node.right);
|
||||
|
||||
int leftNum = 0, rightNum = 0;
|
||||
if (node.left != null && node.left.val == node.val) {
|
||||
leftNum = leftPath + 1;
|
||||
}
|
||||
if (node.right != null && node.right.val == node.val) {
|
||||
rightNum = rightPath + 1;
|
||||
}
|
||||
result = Math.max(result, leftNum + rightNum);
|
||||
return Math.max(leftNum, rightNum);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Scanner sc = new Scanner(System.in);
|
||||
int n = sc.nextInt();
|
||||
sc.nextLine(); // consume the newline character
|
||||
List<String> levelOrder = new ArrayList<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
levelOrder.add(sc.next());
|
||||
}
|
||||
TreeNode root = constructBinaryTree(levelOrder);
|
||||
dfs(root);
|
||||
System.out.println(result);
|
||||
sc.close();
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### python
|
||||
|
||||
```python
|
||||
from typing import List, Optional
|
||||
from collections import deque
|
||||
import sys
|
||||
|
||||
class TreeNode:
|
||||
def __init__(self, val: int = 0, left: 'TreeNode' = None, right: 'TreeNode' = None):
|
||||
self.val = val
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
def construct_binary_tree(level_order: List[str]) -> Optional[TreeNode]:
|
||||
if not level_order:
|
||||
return None
|
||||
|
||||
root = TreeNode(int(level_order[0]))
|
||||
queue = deque([root])
|
||||
i = 1
|
||||
|
||||
while queue and i < len(level_order):
|
||||
current = queue.popleft()
|
||||
|
||||
if i < len(level_order) and level_order[i] != "null":
|
||||
current.left = TreeNode(int(level_order[i]))
|
||||
queue.append(current.left)
|
||||
i += 1
|
||||
|
||||
if i < len(level_order) and level_order[i] != "null":
|
||||
current.right = TreeNode(int(level_order[i]))
|
||||
queue.append(current.right)
|
||||
i += 1
|
||||
|
||||
return root
|
||||
|
||||
result = 0
|
||||
|
||||
def dfs(node: Optional[TreeNode]) -> int:
|
||||
global result
|
||||
if node is None:
|
||||
return 0
|
||||
|
||||
left_path = dfs(node.left)
|
||||
right_path = dfs(node.right)
|
||||
|
||||
left_num = right_num = 0
|
||||
if node.left is not None and node.left.val == node.val:
|
||||
left_num = left_path + 1
|
||||
if node.right is not None and node.right.val == node.val:
|
||||
right_num = right_path + 1
|
||||
|
||||
result = max(result, left_num + right_num)
|
||||
return max(left_num, right_num)
|
||||
|
||||
if __name__ == "__main__":
|
||||
input = sys.stdin.read
|
||||
data = input().strip().split()
|
||||
|
||||
n = int(data[0])
|
||||
level_order = data[1:]
|
||||
|
||||
root = construct_binary_tree(level_order)
|
||||
dfs(root)
|
||||
print(result)
|
||||
|
||||
|
||||
```
|
66
problems/kamacoder/0144.字典序最小的01字符串.md
Normal file
66
problems/kamacoder/0144.字典序最小的01字符串.md
Normal file
@ -0,0 +1,66 @@
|
||||
|
||||
# 0144.字典序最小的01字符串
|
||||
|
||||
贪心思路:移动尽可能 移动前面的1 ,这样可以是 字典序最小
|
||||
|
||||
从前到后遍历,遇到 0 ,就用前面的 1 来交换
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
int main() {
|
||||
int n,k;
|
||||
cin >> n >> k;
|
||||
string s;
|
||||
cin >> s;
|
||||
for(int i = 0; i < n && k > 0; i++) {
|
||||
if(s[i] == '0') {
|
||||
// 开始用前面的 1 来交换
|
||||
int j = i;
|
||||
while(j > 0 && s[j - 1] == '1' && k > 0) {
|
||||
swap(s[j], s[j - 1]);
|
||||
--j;
|
||||
--k;
|
||||
}
|
||||
}
|
||||
}
|
||||
cout << s << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Java:
|
||||
|
||||
```Java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int n = scanner.nextInt();
|
||||
int k = scanner.nextInt();
|
||||
scanner.nextLine(); // 消耗掉换行符
|
||||
String s = scanner.nextLine();
|
||||
char[] ch = s.toCharArray();
|
||||
|
||||
for (int i = 0; i < n && k > 0; i++) {
|
||||
if (ch[i] == '0') {
|
||||
// 开始用前面的 1 来交换
|
||||
int j = i;
|
||||
while (j > 0 && ch[j - 1] == '1' && k > 0) {
|
||||
char tmp = ch[j];
|
||||
ch[j] = ch[j - 1];
|
||||
ch[j - 1] = tmp;
|
||||
j--;
|
||||
k--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(new String(ch));
|
||||
}
|
||||
}
|
||||
```
|
98
problems/kamacoder/0145.数组子序列的排列.md
Normal file
98
problems/kamacoder/0145.数组子序列的排列.md
Normal file
@ -0,0 +1,98 @@
|
||||
|
||||
# 145. 数组子序列的排列
|
||||
|
||||
每个元素出现的次数相乘就可以了。
|
||||
|
||||
注意 “长度为 m 的数组,1 到 m 每个元素都出现过,且恰好出现 1 次。” ,题目中有n个元素,所以我们要统计的就是 1 到 n 元素出现的个数。
|
||||
|
||||
因为如果有一个元素x 大于n了, 那不可能出现 长度为x的数组 且 1 到 x 每个元素都出现过。
|
||||
|
||||
```CPP
|
||||
#include "bits/stdc++.h"
|
||||
using namespace std;
|
||||
int main(){
|
||||
int n;
|
||||
int x;
|
||||
cin >> n;
|
||||
unordered_map<int, int> umap;
|
||||
for(int i = 0; i < n; ++i){
|
||||
cin >> x;
|
||||
if(umap.find(x) != umap.end()) umap[x]++;
|
||||
else umap[x] = 1;
|
||||
}
|
||||
long long res = 0;
|
||||
long long num = 1;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
if (umap.find(i) == umap.end()) break; // 如果i都没出现,后面得数也不能 1 到 m 每个元素都出现过
|
||||
num = (num * umap[i]) % 1000000007;
|
||||
res += num;
|
||||
res %= 1000000007;
|
||||
}
|
||||
cout << res << endl;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```Java
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner sc = new Scanner(System.in);
|
||||
int n = sc.nextInt();
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
int x = sc.nextInt();
|
||||
map.put(x, map.getOrDefault(x, 0) + 1);
|
||||
}
|
||||
long res = 0;
|
||||
long num = 1;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
if (!map.containsKey(i)) break; // 如果i都没出现,后面得数也不能1到m每个元素都出现过
|
||||
num = (num * map.get(i)) % 1000000007;
|
||||
res += num;
|
||||
res %= 1000000007;
|
||||
}
|
||||
System.out.println(res);
|
||||
sc.close();
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
def main():
|
||||
import sys
|
||||
input = sys.stdin.read
|
||||
data = input().split()
|
||||
|
||||
n = int(data[0])
|
||||
umap = {}
|
||||
|
||||
for i in range(1, n + 1):
|
||||
x = int(data[i])
|
||||
if x in umap:
|
||||
umap[x] += 1
|
||||
else:
|
||||
umap[x] = 1
|
||||
|
||||
res = 0
|
||||
num = 1
|
||||
MOD = 1000000007
|
||||
|
||||
for i in range(1, n + 1):
|
||||
if i not in umap:
|
||||
break # 如果i都没出现,后面得数也不能1到m每个元素都出现过
|
||||
num = (num * umap[i]) % MOD
|
||||
res = (res + num) % MOD
|
||||
|
||||
print(res)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
```
|
65
problems/kamacoder/0146.传送树.md
Normal file
65
problems/kamacoder/0146.传送树.md
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
|
||||
|
||||
|
||||
# 146. 传送树
|
||||
|
||||
本题题意是比较绕的,我后面给补上了 【提示信息】对 题目输出样例讲解一下,相对会容易理解的多。
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
vector<vector<int>> edge; // 邻接表来存图
|
||||
vector<int> nxt;
|
||||
int n;
|
||||
|
||||
/*
|
||||
* 递归函数,用于找到每个节点的下一个传送门节点,并记录在nxt数组中。
|
||||
* 遍历当前节点的所有子节点,递归调用findNext以确保子节点的nxt值已经计算出来。
|
||||
* 更新当前节点的nxt值为其子节点中编号最小的节点。
|
||||
* 如果当前节点是叶子节点(即没有子节点),则将其nxt值设置为自身。
|
||||
*/
|
||||
void findNext(int node) {
|
||||
for (int v : edge[node]) {
|
||||
findNext(v);
|
||||
if (nxt[node] == -1 || nxt[node] > min(v, nxt[v])) {
|
||||
nxt[node] = min(v, nxt[v]);
|
||||
}
|
||||
}
|
||||
|
||||
// 叶子节点
|
||||
if (nxt[node] == -1) {
|
||||
nxt[node] = node;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算从节点u出发经过若干次传送门到达叶子节点所需的步数。
|
||||
// 通过不断访问nxt节点,直到到达叶子节点,记录访问的节点数。
|
||||
int get(int u) {
|
||||
int cnt = 1;
|
||||
while (nxt[u] != u) {
|
||||
cnt++;
|
||||
u = nxt[u];
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int main() {
|
||||
cin >> n;
|
||||
edge.resize(n + 1);
|
||||
nxt.resize(n + 1, -1);
|
||||
for (int i = 1; i <= n; ++i) {
|
||||
int a, b;
|
||||
cin >> a >> b;
|
||||
edge[a].push_back(b);
|
||||
}
|
||||
findNext(1);
|
||||
for (int i = 1; i <= n; ++i) {
|
||||
cout << get(i) << ' ';
|
||||
}
|
||||
}
|
||||
|
||||
```
|
78
problems/kamacoder/0147.三珠互斥.md
Normal file
78
problems/kamacoder/0147.三珠互斥.md
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
# 三珠互斥
|
||||
|
||||
1. 如果k * 3 大于 n 了,那说明一定没结果,如果没想明白,大家举个例子试试看
|
||||
2. 分别求出三个红珠子之间的距离
|
||||
3. 对这三段距离从小到大排序 y1, y2, y3
|
||||
4. 如果第一段距离y1 小于k,说明需要交换 k - y 次, 同理 第二段距离y2 小于k,说明需要交换 k - y2 次
|
||||
5. y1 y2 都调整好了,不用计算y3,因为 y3是距离最大
|
||||
|
||||
```CPP
|
||||
#include<bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
int main(){
|
||||
int t;
|
||||
cin >> t;
|
||||
int n, k, a1, a2, a3;
|
||||
vector<int> dis(3);
|
||||
|
||||
while (t--) {
|
||||
cin >> n >> k >> a1 >> a2 >> a3;
|
||||
if(k * 3 > n){
|
||||
cout << -1 << endl;
|
||||
continue;
|
||||
}
|
||||
dis[0] = min(abs(a1 - a2), n - abs(a1 - a2));
|
||||
dis[1] = min(abs(a1 - a3), n - abs(a1 - a3));
|
||||
dis[2] = min(abs(a3 - a2), n - abs(a3 - a2));
|
||||
|
||||
sort(dis.begin(), dis.end());
|
||||
|
||||
int result = 0;
|
||||
if (dis[0] < k) result += (k - dis[0]);
|
||||
if (dis[1] < k) result += (k - dis[1]);
|
||||
|
||||
cout << result << endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Java代码:
|
||||
|
||||
```Java
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int t = scanner.nextInt();
|
||||
|
||||
while (t-- > 0) {
|
||||
int n = scanner.nextInt();
|
||||
int k = scanner.nextInt();
|
||||
int a1 = scanner.nextInt();
|
||||
int a2 = scanner.nextInt();
|
||||
int a3 = scanner.nextInt();
|
||||
if (k * 3 > n) {
|
||||
System.out.println(-1);
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Integer> dis = new ArrayList<>(3);
|
||||
dis.add(Math.min(Math.abs(a1 - a2), n - Math.abs(a1 - a2)));
|
||||
dis.add(Math.min(Math.abs(a1 - a3), n - Math.abs(a1 - a3)));
|
||||
dis.add(Math.min(Math.abs(a3 - a2), n - Math.abs(a3 - a2)));
|
||||
|
||||
Collections.sort(dis);
|
||||
|
||||
int result = 0;
|
||||
if (dis.get(0) < k) result += (k - dis.get(0));
|
||||
if (dis.get(1) < k) result += (k - dis.get(1));
|
||||
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
122
problems/kamacoder/0148.扑克牌同花顺.md
Normal file
122
problems/kamacoder/0148.扑克牌同花顺.md
Normal file
@ -0,0 +1,122 @@
|
||||
|
||||
# 扑克牌同花顺
|
||||
|
||||
首先我们要定义一个结构体,来存放我们的数据
|
||||
|
||||
`map<花色,{同一花色牌集合,同一花色的牌对应的牌数量}>`
|
||||
|
||||
再遍历 每一个花色下,每一个牌 的数量
|
||||
|
||||
代码如下详细注释:
|
||||
|
||||
|
||||
```CPP
|
||||
#include<bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
string cards[] = {"H","S","D","C"};
|
||||
typedef long long ll;
|
||||
struct color
|
||||
{
|
||||
set<int> st; // 同一花色 牌的集合
|
||||
map<int, ll> cnt; // 同一花色 牌对应的数量
|
||||
};
|
||||
unordered_map<string, color> umap;
|
||||
|
||||
int main() {
|
||||
int n;
|
||||
cin >> n;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int x, y;
|
||||
string card;
|
||||
cin >> x >> y >> card;
|
||||
umap[card].st.insert(x);
|
||||
umap[card].cnt[x] += y;
|
||||
}
|
||||
ll sum = 0;
|
||||
// 遍历每一个花色
|
||||
for (string cardOne : cards) {
|
||||
color colorOne = umap[cardOne];
|
||||
// 遍历 同花色 每一个牌
|
||||
for (int number : colorOne.st) {
|
||||
ll numberCount = colorOne.cnt[number]; // 获取牌为number的数量是 numberCount
|
||||
|
||||
// 统计 number 到 number + 4 都是否有牌,用cal 把 number 到number+4 的数量记下来
|
||||
ll cal = numberCount;
|
||||
for (int j = number + 1; j <= number + 4; j++) cal = min(cal, colorOne.cnt[j]);
|
||||
// 统计结果
|
||||
sum += cal;
|
||||
// 把统计过的同花顺数量减下去
|
||||
for (int j = number + 1; j <= number + 4; j++) colorOne.cnt[j] -= cal;
|
||||
}
|
||||
}
|
||||
cout << sum << endl;
|
||||
}
|
||||
```
|
||||
|
||||
Java代码如下:
|
||||
|
||||
```Java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
static String[] cards = {"H", "S", "D", "C"}; // 花色数组
|
||||
|
||||
static class Color {
|
||||
Set<Integer> st; // 同一花色牌的集合
|
||||
Map<Integer, Long> cnt; // 同一花色牌对应的数量
|
||||
|
||||
Color() {
|
||||
st = new HashSet<>(); // 初始化集合
|
||||
cnt = new HashMap<>(); // 初始化映射
|
||||
}
|
||||
}
|
||||
|
||||
static Map<String, Color> umap = new HashMap<>(); // 用于存储每种花色对应的Color对象
|
||||
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int n = scanner.nextInt(); // 读取牌的数量
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
int x = scanner.nextInt(); // 读取牌的值
|
||||
int y = scanner.nextInt(); // 读取牌的数量
|
||||
String card = scanner.next(); // 读取牌的花色
|
||||
|
||||
umap.putIfAbsent(card, new Color()); // 如果不存在该花色,则创建一个新的Color对象
|
||||
umap.get(card).st.add(x); // 将牌的值加入集合
|
||||
umap.get(card).cnt.put(x, umap.get(card).cnt.getOrDefault(x, 0L) + y); // 更新牌的数量
|
||||
}
|
||||
|
||||
long sum = 0; // 结果累加器
|
||||
|
||||
// 遍历每一种花色
|
||||
for (String cardOne : cards) {
|
||||
Color colorOne = umap.getOrDefault(cardOne, new Color()); // 获取对应花色的Color对象
|
||||
|
||||
// 遍历同花色的每一张牌
|
||||
for (int number : colorOne.st) {
|
||||
long numberCount = colorOne.cnt.get(number); // 获取当前牌的数量
|
||||
|
||||
// 计算从当前牌到number+4的最小数量
|
||||
long cal = numberCount;
|
||||
for (int j = number + 1; j <= number + 4; j++) {
|
||||
cal = Math.min(cal, colorOne.cnt.getOrDefault(j, 0L)); // 更新cal为最小值
|
||||
}
|
||||
|
||||
// 将结果累加到sum
|
||||
sum += cal;
|
||||
|
||||
// 将统计过的同花顺数量减去
|
||||
for (int j = number + 1; j <= number + 4; j++) {
|
||||
colorOne.cnt.put(j, colorOne.cnt.getOrDefault(j, 0L) - cal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(sum); // 输出结果
|
||||
}
|
||||
}
|
||||
|
||||
```
|
102
problems/kamacoder/0149.好数组.md
Normal file
102
problems/kamacoder/0149.好数组.md
Normal file
@ -0,0 +1,102 @@
|
||||
|
||||
# 149. 好数组
|
||||
|
||||
贪心思路:
|
||||
|
||||
整体思路是移动到中间位置(中位数),一定是 移动次数最小的。
|
||||
|
||||
有一个数可以不改变,对数组排序之后, 最小数 和 最大数 一定是移动次数最多的,所以分别保留最小 和 最大的不变。
|
||||
|
||||
中间可能有两个位置,所以要计算中间偏前 和 中间偏后的
|
||||
|
||||
代码如下:
|
||||
|
||||
```CPP
|
||||
#include<bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
int n;
|
||||
cin >> n;
|
||||
vector<long> arr(n);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
cin >> arr[i];
|
||||
}
|
||||
sort(arr.begin(), arr.end());
|
||||
|
||||
if (arr[0] == arr[n - 1]) {
|
||||
cout << 1 << endl;
|
||||
return 0;
|
||||
}
|
||||
long cnt = 0L;
|
||||
long cnt1 = 0L;
|
||||
|
||||
// 如果要保留一个不改变,要不不改最小的,要不不改最大的。
|
||||
|
||||
// 取中间偏前的位置
|
||||
long mid = arr[(n - 2) / 2];
|
||||
|
||||
// 不改最大的
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
cnt += abs(arr[i] - mid);
|
||||
}
|
||||
|
||||
// 取中间偏后的位置
|
||||
mid = arr[n / 2];
|
||||
|
||||
// 不改最小的
|
||||
for (int i = 1; i < n; i++) {
|
||||
cnt1 += abs(arr[i] - mid);
|
||||
}
|
||||
|
||||
cout << min(cnt, cnt1) << endl;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Java代码如下:
|
||||
|
||||
```Java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int n = scanner.nextInt();
|
||||
long[] arr = new long[n];
|
||||
for (int i = 0; i < n; ++i) {
|
||||
arr[i] = scanner.nextLong();
|
||||
}
|
||||
Arrays.sort(arr);
|
||||
|
||||
if (arr[0] == arr[n - 1]) {
|
||||
System.out.println(1);
|
||||
return;
|
||||
}
|
||||
long cnt = 0L;
|
||||
long cnt1 = 0L;
|
||||
|
||||
// 如果要保留一个不改变,要不不改最小的,要不不改最大的。
|
||||
|
||||
// 取中间偏前的位置
|
||||
long mid = arr[(n - 2) / 2];
|
||||
|
||||
// 不改最大的
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
cnt += Math.abs(arr[i] - mid);
|
||||
}
|
||||
|
||||
// 取中间偏后的位置
|
||||
mid = arr[n / 2];
|
||||
|
||||
// 不改最小的
|
||||
for (int i = 1; i < n; i++) {
|
||||
cnt1 += Math.abs(arr[i] - mid);
|
||||
}
|
||||
|
||||
System.out.println(Math.min(cnt, cnt1));
|
||||
}
|
||||
}
|
||||
|
||||
```
|
66
problems/kamacoder/0150.极长连续段的权值.md
Normal file
66
problems/kamacoder/0150.极长连续段的权值.md
Normal file
@ -0,0 +1,66 @@
|
||||
|
||||
# 150. 极长连续段的权值
|
||||
|
||||
动态规划,枚举最后边节点的情况:
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
int n;
|
||||
cin >> n;
|
||||
string s;
|
||||
cin >> s;
|
||||
|
||||
long long result = 1;
|
||||
long long a = 1;
|
||||
|
||||
for (int i = 1; i < n; ++i) {
|
||||
// 加上本身长度为1的子串
|
||||
if (s[i] == s[i - 1]) {
|
||||
a += 1;
|
||||
result += a;
|
||||
// 以最右节点为终点,每个子串的级长连续段都+1,再加本身长度为1的子串
|
||||
} else {
|
||||
a = a + i + 1;
|
||||
result += a;
|
||||
}
|
||||
}
|
||||
cout << result << endl;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Java代码如下:
|
||||
|
||||
```Java
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int n = scanner.nextInt();
|
||||
String s = scanner.next();
|
||||
|
||||
long result = 1;
|
||||
long a = 1;
|
||||
|
||||
for (int i = 1; i < n; ++i) {
|
||||
// 加上本身长度为1的子串
|
||||
if (s.charAt(i) == s.charAt(i - 1)) {
|
||||
a += 1;
|
||||
result += a;
|
||||
// 以最右节点为终点,每个子串的级长连续段都+1,再加本身长度为1的子串
|
||||
} else {
|
||||
a = a + i + 1;
|
||||
result += a;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
127
problems/kamacoder/0151.手机流畅运行的秘密.md
Normal file
127
problems/kamacoder/0151.手机流畅运行的秘密.md
Normal file
@ -0,0 +1,127 @@
|
||||
# 151. 手机流畅运行的秘密
|
||||
|
||||
[题目链接](https://kamacoder.com/problempage.php?pid=1229)
|
||||
|
||||
先运行 能留下电量多的 任务,才能有余电运行其他任务。
|
||||
|
||||
任务1,1:10 ,运行完 能留下 9个电
|
||||
|
||||
任务2,2:12,运行完 能留下 10个电
|
||||
|
||||
任务3,3:10,运行完 能留下 7个电。
|
||||
|
||||
运行顺序: 任务2 -> 任务1 -> 任务3
|
||||
|
||||
按照 最低初始电量 - 耗电量,从大到小排序。
|
||||
|
||||
计算总电量,需要 从小到大 遍历, 不断取 总电量 + 任务耗电量 与 任务最低初始电量 的最大值。
|
||||
|
||||
```CPP
|
||||
#include<bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
bool cmp(const pair<int,int>& taskA, const pair<int,int>& taskB) {
|
||||
return (taskA.second - taskA.first) < (taskB.second - taskB.first);
|
||||
}
|
||||
int main() {
|
||||
string str, tmp;
|
||||
vector<pair<int,int>> tasks;
|
||||
|
||||
//处理输入
|
||||
getline(cin, str);
|
||||
stringstream ss(str);
|
||||
while (getline(ss, tmp, ',')) {
|
||||
int p = tmp.find(":");
|
||||
string a = tmp.substr(0, p);
|
||||
string b = tmp.substr(p + 1);
|
||||
tasks.push_back({stoi(a), stoi(b)});
|
||||
}
|
||||
|
||||
// 按照差值从小到大排序
|
||||
sort(tasks.begin(), tasks.end(), cmp);
|
||||
|
||||
// 收集结果
|
||||
int result = 0;
|
||||
for (int i = 0 ; i < tasks.size(); i++) {
|
||||
result = max(result + tasks[i].first, tasks[i].second);
|
||||
}
|
||||
|
||||
result = result <= 4800 ? result : -1;
|
||||
cout << result << endl;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Java版本:
|
||||
|
||||
```Java
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner sc = new Scanner(System.in);
|
||||
String str = sc.nextLine();
|
||||
String[] tasksArray = str.split(",");
|
||||
List<Pair> tasks = Arrays.stream(tasksArray)
|
||||
.map(task -> {
|
||||
String[] parts = task.split(":");
|
||||
return new Pair(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 按照差值从小到大排序
|
||||
Collections.sort(tasks, (taskA, taskB) ->
|
||||
(taskA.second - taskA.first) - (taskB.second - taskB.first)
|
||||
);
|
||||
|
||||
// 收集结果
|
||||
int result = 0;
|
||||
for (Pair task : tasks) {
|
||||
result = Math.max(result + task.first, task.second);
|
||||
}
|
||||
|
||||
result = result <= 4800 ? result : -1;
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
|
||||
class Pair {
|
||||
int first;
|
||||
int second;
|
||||
|
||||
Pair(int first, int second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Python版本:
|
||||
|
||||
```python
|
||||
def main():
|
||||
import sys
|
||||
input = sys.stdin.read
|
||||
|
||||
str = input().strip()
|
||||
tasks = []
|
||||
for tmp in str.split(','):
|
||||
a, b = map(int, tmp.split(':'))
|
||||
tasks.append((a, b))
|
||||
|
||||
# 按照差值从小到大排序
|
||||
tasks.sort(key=lambda task: task[1] - task[0])
|
||||
|
||||
# 收集结果
|
||||
result = 0
|
||||
for task in tasks:
|
||||
result = max(result + task[0], task[1])
|
||||
|
||||
result = result if result <= 4800 else -1
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
121
problems/kamacoder/0152.小米手机通信校准.md
Normal file
121
problems/kamacoder/0152.小米手机通信校准.md
Normal file
@ -0,0 +1,121 @@
|
||||
|
||||
|
||||
# 152. 小米手机通信校准
|
||||
|
||||
[题目链接](https://kamacoder.com/problempage.php?pid=1230)
|
||||
|
||||
一道模拟题,但比较考察 代码能力。
|
||||
|
||||
遍历去找 里 freq 最近的 freg就好, 需要记录刚遍历过的的freg和 loss,因为可能有 相邻一样的 freg。
|
||||
|
||||
```CPP
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
int freq;
|
||||
cin >> freq;
|
||||
string data;
|
||||
double result = 0;
|
||||
int last_freg = 0; // 记录上一个 freg
|
||||
int last_loss = 0; // 记录上一个loss
|
||||
while(cin >> data) {
|
||||
int index = data.find(':');
|
||||
int freg = stoi(data.substr(0, index)); // 获取 freg 和 loss
|
||||
int loss = stoi(data.substr(index + 1));
|
||||
// 两遍一样
|
||||
if(abs(freg - freq) == abs(last_freg - freq)) {
|
||||
result = (double)(last_loss + loss)/2.0;
|
||||
} // 否则更新最新的result
|
||||
else if(abs(freg - freq) < abs(last_freg - freq)){
|
||||
result = (double)loss;
|
||||
}
|
||||
last_freg = freg;
|
||||
last_loss = loss;
|
||||
}
|
||||
printf("%.1lf\n", result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Java 版本:
|
||||
|
||||
```Java
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner sc = new Scanner(System.in);
|
||||
int freq = sc.nextInt();
|
||||
sc.nextLine(); // 读取换行符
|
||||
|
||||
String inputLine = sc.nextLine(); // 读取包含所有后续输入的行
|
||||
String[] data = inputLine.split(" "); // 根据空格分割输入
|
||||
|
||||
double result = 0;
|
||||
int lastFreq = 0; // 记录上一个 freg
|
||||
int lastLoss = 0; // 记录上一个 loss
|
||||
|
||||
for (String entry : data) {
|
||||
int index = entry.indexOf(':');
|
||||
int freg = Integer.parseInt(entry.substring(0, index)); // 获取 freg 和 loss
|
||||
int loss = Integer.parseInt(entry.substring(index + 1));
|
||||
|
||||
// 两遍一样
|
||||
if (Math.abs(freg - freq) == Math.abs(lastFreq - freq)) {
|
||||
result = (double) (lastLoss + loss) / 2.0;
|
||||
}
|
||||
// 否则更新最新的 result
|
||||
else if (Math.abs(freg - freq) < Math.abs(lastFreq - freq)) {
|
||||
result = (double) loss;
|
||||
}
|
||||
|
||||
lastFreq = freg;
|
||||
lastLoss = loss;
|
||||
}
|
||||
|
||||
System.out.printf("%.1f\n", result);
|
||||
sc.close();
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Python版本:
|
||||
|
||||
```python
|
||||
def main():
|
||||
import sys
|
||||
input = sys.stdin.read
|
||||
data = input().split()
|
||||
|
||||
freq = int(data[0])
|
||||
result = 0
|
||||
last_freg = 0 # 记录上一个 freg
|
||||
last_loss = 0 # 记录上一个 loss
|
||||
|
||||
for i in range(1, len(data)):
|
||||
item = data[i]
|
||||
index = item.find(':')
|
||||
freg = int(item[:index]) # 获取 freg 和 loss
|
||||
loss = int(item[index + 1:])
|
||||
|
||||
# 两遍一样
|
||||
if abs(freg - freq) == abs(last_freg - freq):
|
||||
result = (last_loss + loss) / 2.0
|
||||
# 否则更新最新的 result
|
||||
elif abs(freg - freq) < abs(last_freg - freq):
|
||||
result = loss
|
||||
|
||||
last_freg = freg
|
||||
last_loss = loss
|
||||
|
||||
print(f"{result:.1f}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
```
|
93
problems/kamacoder/图论为什么用ACM模式.md
Normal file
93
problems/kamacoder/图论为什么用ACM模式.md
Normal file
@ -0,0 +1,93 @@
|
||||
|
||||
|
||||
|
||||
# 图论为什么统一使用ACM模式
|
||||
|
||||
代码随想录图论章节给大家统一换成ACM输入输出模式。
|
||||
|
||||
图论是在笔试还有面试中,通常都是以ACM模式来考察大家,而大家习惯在力扣刷题(核心代码模式),核心代码模式对图的存储和输出都隐藏了。
|
||||
|
||||
而图论题目的输出输出 相对其他类型题目来说是最难处理的。
|
||||
|
||||
ACM模式是最考察候选人对代码细节把控程度的, 图的构成,图的输出,这些只有ACM输入输出模式才能体现出来。
|
||||
|
||||
### 输入的细节
|
||||
|
||||
图论的输入难在 图的存储结构,**如果没有练习过 邻接表和邻接矩阵 ,很多录友是写不出来的**。
|
||||
|
||||
而力扣上是直接给好现成的 数据结构,可以直接用,所以练习不到图的输入,也练习不到邻接表和邻接矩阵。
|
||||
|
||||
**如果连邻接表 和 邻接矩阵都不知道或者写不出来的话,可以说 图论没有入门**。
|
||||
|
||||
举个例子,对于力扣 [797.所有可能的路径](https://leetcode.cn/problems/all-paths-from-source-to-target/description/) ,录友了解深度优先搜索之后,这道题目就是模板题,是送分题。
|
||||
|
||||
如果面试的时候出一道原题 (笔试都是ACM模式,部分面试也是ACM模式),不少熟练刷力扣的录友都难住了,**因为不知道图应该怎么存,也不知道自己存的图如何去遍历**。
|
||||
|
||||
即使面试的时候,有的面试官,让你用核心代码模式做题,当你写出代码后,**面试官补充一句:这个图 你是怎么存的**?
|
||||
|
||||
难道和面试官说:我只知道图的算法,但我不知道图怎么存。
|
||||
|
||||
后面大家在刷 代码随想录图论第一题[98. 所有可达路径](./0098.所有可达路径.md) 的时候,就可以感受到图存储的难点所在。
|
||||
|
||||
所以这也是为什么我要让大家练习 ACM模式,也是我为什么 在代码随想录图论讲解中,不惜自己亲自出题,让大家统一练习ACM模式。
|
||||
|
||||
### 输出的细节
|
||||
|
||||
同样,图论的输出也有细节,例如 求节点1 到节点5的所有路径, 输出可能是:
|
||||
|
||||
```
|
||||
1 2 4 5
|
||||
1 3 5
|
||||
```
|
||||
|
||||
表示有两条路可以到节点5, 那储存这个结果需要二维数组,最后在一起输出,力扣是直接return数组就好了,但 ACM模式要求我们自己输出,这里有就细节了。
|
||||
|
||||
就拿 只输出一行数据,输出 `1 2 4 5` 来说,
|
||||
|
||||
很多录友代码可能直接就这么写了:
|
||||
|
||||
```CPP
|
||||
for (int i = 0 ; i < result.size(); i++) {
|
||||
cout << result[i] << " ";
|
||||
}
|
||||
```
|
||||
|
||||
这么写输出的结果是 `1 2 4 5 `, 发现结果是对的,一提交,发现OJ返回 格式错误 或者 结果错误。
|
||||
|
||||
如果没练习过这种输出方式的录友,就开始怀疑了,这结果一样一样的,怎么就不对,我在力扣上提交都是对的!
|
||||
|
||||
**大家要注意,5 后面要不要有空格**!
|
||||
|
||||
上面这段代码输出,5后面是加上了空格了,如果判题机判断 结果的长度,标准答案`1 2 4 5`长度是7,而上面代码输出的长度是 8,很明显就是不对的。
|
||||
|
||||
所以正确的写法应该是:
|
||||
|
||||
```CPP
|
||||
for (int i = 0 ; i < result.size() - 1; i++) {
|
||||
cout << result[i] << " ";
|
||||
}
|
||||
cout << result[result.size() - 1];
|
||||
```
|
||||
|
||||
这么写,最后一个元素后面就没有空格了。
|
||||
|
||||
这是很多录友经常疏忽的,也是大家刷习惯了 力扣(核心代码模式)根本不会注意到的细节。
|
||||
|
||||
**同样在工程开发中,这些细节都是影响系统稳定运行的因素之一**。
|
||||
|
||||
**ACM模式 除了考验算法思路,也考验 大家对 代码的把控力度**, 而 核心代码模式 只注重算法的解题思路,所以输入输出这些就省略掉了。
|
||||
|
||||
|
||||
### 其他
|
||||
|
||||
**大家如果熟练ACM模式,那么核心代码模式没问题,但反过来就不一定了**。
|
||||
|
||||
而且我在讲解图论的时候,最头疼的就是找题,在力扣上 找题总是找不到符合思路且来完整表达算法精髓的题目。
|
||||
|
||||
特别是最短路算法相关的题目,例如 Bellman_ford系列 ,Floyd ,A * 等等总是找不到符合思路的题目。
|
||||
|
||||
索性统一我自己来出题,这其中也是巨大的工作量。为了给大家带来极致的学习体验,我在很多细节上都下了功夫。
|
||||
|
||||
等大家将图论刷完,就会感受到我的良苦用心。加油
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
这一篇我们正式开始图论!
|
||||
|
||||
代码随想录图论中的算法题目将统一使用ACM模式,[为什么要使用ACM模式](./图论为什么用ACM模式.md)
|
||||
|
||||
## 图的基本概念
|
||||
|
||||
二维坐标中,两点可以连成线,多个点连成的线就构成了图。
|
||||
|
104
problems/kamacoder/好二叉树.md
Normal file
104
problems/kamacoder/好二叉树.md
Normal file
@ -0,0 +1,104 @@
|
||||
|
||||
本题和 [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) 比较像
|
||||
|
||||
* 取模这里很容易出错
|
||||
* 过程中所用到的数值都有可能超过int,所以要改用longlong
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
long long mod = 1e9 + 7;
|
||||
long long dp(int t, vector<long long>& memory) {
|
||||
if (t % 2 == 0) return 0;
|
||||
if (t == 1) return 1;
|
||||
if (memory[t] != -1) return memory[t];
|
||||
|
||||
long long result = 0;
|
||||
// 枚举左右子树节点的数量
|
||||
for (int i = 1; i < t; i += 2) {
|
||||
long long leftNum = dp(i, memory); // 左子树节点数量为i
|
||||
long long rightNum = dp(t - i - 1, memory); // 右子树节点数量为t - i - 1
|
||||
result += (leftNum * rightNum) % mod; // 注意这里是乘的关系
|
||||
result %= mod;
|
||||
}
|
||||
memory[t] = result;
|
||||
return result;
|
||||
}
|
||||
int main() {
|
||||
int n;
|
||||
cin >> n;
|
||||
vector<long long> memory(n + 1, -1);
|
||||
cout << dp(n, memory) << endl;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const int MOD = 1000000007;
|
||||
|
||||
int main() {
|
||||
int num;
|
||||
cin >> num;
|
||||
|
||||
if (num % 2 == 0) {
|
||||
cout << 0 << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
vector<long long> dp(num + 1, 0);
|
||||
dp[1] = 1;
|
||||
|
||||
for (int i = 3; i <= num; i += 2) {
|
||||
for (int j = 1; j <= i - 2; j += 2) {
|
||||
dp[i] = (dp[i] + dp[j] * dp[i - 1 - j]) % MOD;
|
||||
}
|
||||
}
|
||||
|
||||
cout << dp[num] << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
第二题的代码
|
||||
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
long fastexp(long base,long n,long mod){
|
||||
long answer = 1;
|
||||
while(n > 0){
|
||||
if(n % 2 == 1){
|
||||
answer = (answer * base) % mod;
|
||||
}
|
||||
base = (base * base) % mod;
|
||||
n /= 2;
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
int kawaiiStrings(int n) {
|
||||
// write code here
|
||||
std::vector<long> f(n + 1), g(n + 1), h(n + 1);
|
||||
long mod = 1000000007;
|
||||
for (long i = 2; i <= n; i++) g[i] = (g[i - 1] * 2 + (i - 1) * fastexp(2,i-2,mod)) % mod;
|
||||
for (long i = 3; i <= n; i++) f[i] = ((f[i - 1] * 3) % mod + g[i - 1]) % mod;
|
||||
for (long i = 3; i <= n; i++) h[i] = (fastexp(3, i - 3, mod) + h[i - 1] * 3 - h[i - 3]) % mod;
|
||||
return (f[n]-h[n]+mod)%mod;
|
||||
|
||||
}
|
||||
|
||||
int main(){
|
||||
int n;
|
||||
cin >> n;
|
||||
cout << kawaiiStrings(n) << endl;
|
||||
return 0;
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
|
||||
|
||||
```CPP
|
||||
#include<bits/stdc++.h>
|
||||
using namespace std;
|
||||
int main() {
|
||||
int index = 0;
|
||||
long long optNum;
|
||||
string s;
|
||||
string cmd;
|
||||
while(cin >> cmd){
|
||||
//cout << s << endl;
|
||||
if(cmd == "insert"){
|
||||
string buff;
|
||||
cin >> buff;
|
||||
s.insert(index, buff);
|
||||
index += buff.size();
|
||||
}
|
||||
else if(cmd == "move"){
|
||||
cin >> optNum;
|
||||
if(optNum > 0 && index + optNum <= s.size()) index += optNum;
|
||||
if(optNum < 0 && index >= -optNum) index += optNum;
|
||||
}
|
||||
else if(cmd == "delete"){
|
||||
cin >> optNum;
|
||||
if(index >= optNum && optNum != 0){
|
||||
s.erase(index - optNum, optNum);
|
||||
index -= optNum;
|
||||
}
|
||||
}
|
||||
else if(cmd == "copy"){
|
||||
if(index > 0) {
|
||||
string tmp = s.substr(0, index);
|
||||
s.insert(index, tmp);
|
||||
}
|
||||
}
|
||||
else if(cmd == "end"){
|
||||
for(int i = 0; i < index; i++) {
|
||||
cout << s[i];
|
||||
}
|
||||
cout << '|';
|
||||
for(int i = index; i < s.size(); i++){
|
||||
cout << s[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
29
problems/kamacoder/完美数.md
Normal file
29
problems/kamacoder/完美数.md
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
int countOnes(long long num) {
|
||||
int zeroCount = 0;
|
||||
while (num > 0) {
|
||||
if (num % 10 != 0) { // 检查最低位是否为0
|
||||
zeroCount++;
|
||||
}
|
||||
num /= 10; // 移除最低位
|
||||
}
|
||||
return zeroCount;
|
||||
}
|
||||
int main() {
|
||||
int n;
|
||||
cin >> n;
|
||||
vector<int> vec(n);
|
||||
for (int i = 0; i < n; i++) cin >> vec[i];
|
||||
int result = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = i + 1; j < n; j++) {
|
||||
if (countOnes(vec[i] * vec[j]) == 1) result++;
|
||||
}
|
||||
}
|
||||
cout << result << endl;
|
||||
}
|
||||
```
|
@ -1,47 +0,0 @@
|
||||
|
||||
|
||||
|
||||
```CPP
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
using namespace std;
|
||||
int main() {
|
||||
unordered_set<int> uset;
|
||||
int n, a;
|
||||
cin >> n;
|
||||
while (n--) {
|
||||
cin >> a;
|
||||
uset.insert(a);
|
||||
}
|
||||
int m, x, vlan_id;
|
||||
long long tb;
|
||||
vector<long long> vecTB;
|
||||
cin >> m;
|
||||
while(m--) {
|
||||
cin >> tb;
|
||||
cin >> x;
|
||||
vector<long long> vecVlan_id(x);
|
||||
for (int i = 0; i < x; i++) {
|
||||
cin >> vecVlan_id[i];
|
||||
}
|
||||
for (int i = 0; i < x; i++) {
|
||||
if (uset.find(vecVlan_id[i]) != uset.end()) {
|
||||
vecTB.push_back(tb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cout << vecTB.size() << endl;
|
||||
if (vecTB.size() != 0) {
|
||||
sort(vecTB.begin(), vecTB.end());
|
||||
for (int i = 0; i < vecTB.size() ; i++) cout << vecTB[i] << " ";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
```
|
@ -25,7 +25,7 @@
|
||||
index_{left}=(\sum_{i=0}^{i=k}2^i)+2*m-1-1=2^{k+1}+2m-3
|
||||
$$
|
||||
|
||||
- 故左孩子的下表为$index_{left}=index_{father}\times2+1$,同理可得到右子孩子的索引关系。也可以直接在左子孩子的基础上`+1`。
|
||||
- 故左孩子的下标为$index_{left}=index_{father}\times2+1$,同理可得到右子孩子的索引关系。也可以直接在左子孩子的基础上`+1`。
|
||||
|
||||
|
||||
|
||||
|
@ -69,7 +69,7 @@ for (int i = 0; i < array.size(); i++) {
|
||||
|
||||
其实使用双指针也可以解决1.两数之和的问题,只不过1.两数之和求的是两个元素的下标,没法用双指针,如果改成求具体两个元素的数值就可以了,大家可以尝试用双指针做一个leetcode上两数之和的题目,就可以体会到我说的意思了。
|
||||
|
||||
使用了哈希法解决了两数之和,但是哈希法并不使用于三数之和!
|
||||
使用了哈希法解决了两数之和,但是哈希法并不适用于三数之和!
|
||||
|
||||
使用哈希法的过程中要把符合条件的三元组放进vector中,然后在去去重,这样是非常费时的,很容易超时,也是三数之和通过率如此之低的根源所在。
|
||||
|
||||
|
@ -40,7 +40,7 @@
|
||||
|
||||
* 陷阱二
|
||||
|
||||
在一个有序序列求最值的时候,不要定义一个全局遍历,然后遍历序列更新全局变量求最值。因为最值可能就是int 或者 longlong的最小值。
|
||||
在一个有序序列求最值的时候,不要定义一个全局变量,然后遍历序列更新全局变量求最值。因为最值可能就是int 或者 longlong的最小值。
|
||||
|
||||
推荐要通过前一个数值(pre)和后一个数值比较(cur),得出最值。
|
||||
|
||||
|
@ -117,6 +117,13 @@
|
||||
|
||||
相信大家有遇到过这种情况: 感觉题目的边界调节超多,一波接着一波的判断,找边界,拆了东墙补西墙,好不容易运行通过了,代码写的十分冗余,毫无章法,其实**真正解决题目的代码都是简洁的,或者有原则性的**,大家可以在这道题目中体会到这一点。
|
||||
|
||||
### 前缀和
|
||||
|
||||
> 代码随想录后续补充题目
|
||||
|
||||
* [数组:求取区间和](https://programmercarl.com/kamacoder/0058.区间和.html)
|
||||
|
||||
前缀和的思路其实很简单,但非常实用,如果没接触过的录友,也很难想到这个解法维度,所以 这是开阔思路 而难度又不高的好题。
|
||||
|
||||
## 总结
|
||||
|
||||
|
@ -17,21 +17,15 @@
|
||||
|
||||
## 思路
|
||||
|
||||
正式开始讲解背包问题!
|
||||
|
||||
这周我们正式开始讲解背包问题!
|
||||
|
||||
背包问题的经典资料当然是:背包九讲。在公众号「代码随想录」后台回复:背包九讲,就可以获得背包九讲的pdf。
|
||||
|
||||
但说实话,背包九讲对于小白来说确实不太友好,看起来还是有点费劲的,而且都是伪代码理解起来也吃力。
|
||||
|
||||
对于面试的话,其实掌握01背包,和完全背包,就够用了,最多可以再来一个多重背包。
|
||||
对于面试的话,其实掌握01背包和完全背包,就够用了,最多可以再来一个多重背包。
|
||||
|
||||
如果这几种背包,分不清,我这里画了一个图,如下:
|
||||
|
||||

|
||||
|
||||
|
||||
至于背包九讲其他背包,面试几乎不会问,都是竞赛级别的了,leetcode上连多重背包的题目都没有,所以题库也告诉我们,01背包和完全背包就够用了。
|
||||
除此以外其他类型的背包,面试几乎不会问,都是竞赛级别的了,leetcode上连多重背包的题目都没有,所以题库也告诉我们,01背包和完全背包就够用了。
|
||||
|
||||
而完全背包又是也是01背包稍作变化而来,即:完全背包的物品数量是无限的。
|
||||
|
||||
@ -53,7 +47,7 @@ leetcode上没有纯01背包的问题,都是01背包应用方面的题目,
|
||||
|
||||
这样其实是没有从底向上去思考,而是习惯性想到了背包,那么暴力的解法应该是怎么样的呢?
|
||||
|
||||
每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么时间复杂度就是$o(2^n)$,这里的n表示物品数量。
|
||||
每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么时间复杂度就是O(2^n),这里的n表示物品数量。
|
||||
|
||||
**所以暴力的解法是指数级别的时间复杂度。进而才需要动态规划的解法来进行优化!**
|
||||
|
||||
@ -73,30 +67,111 @@ leetcode上没有纯01背包的问题,都是01背包应用方面的题目,
|
||||
|
||||
以下讲解和图示中出现的数字都是以这个例子为例。
|
||||
|
||||
|
||||
### 二维dp数组01背包
|
||||
|
||||
依然动规五部曲分析一波。
|
||||
|
||||
1. 确定dp数组以及下标的含义
|
||||
|
||||
对于背包问题,有一种写法, 是使用二维数组,即**dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少**。
|
||||
我们需要使用二维数组,为什么呢?
|
||||
|
||||
只看这个二维数组的定义,大家一定会有点懵,看下面这个图:
|
||||
因为有两个维度需要表示,分别是:物品 和 背包容量
|
||||
|
||||
如图,二维数组为 dp[i][j]。
|
||||
|
||||

|
||||
|
||||
那么这里 i 、j、dp[i][j] 分别表示什么呢?
|
||||
|
||||
i 来表示物品、j表示背包容量。
|
||||
|
||||
(如果想用j 表示物品,j表示背包容量 行不行? 都可以的,个人习惯而已)
|
||||
|
||||
我们来尝试把上面的 二维表格填写一下。
|
||||
|
||||
动态规划的思路是根据子问题的求解推导出整体的最优解。
|
||||
|
||||
我们先看把物品0 放入背包的情况:
|
||||
|
||||

|
||||
|
||||
背包容量为0,放不下物品0,此时背包里的价值为0。
|
||||
|
||||
背包容量为1,可以放下物品0,此时背包里的价值为15.
|
||||
|
||||
背包容量为2,依然可以放下物品0 (注意 01背包里物品只有一个),此时背包里的价值为15。
|
||||
|
||||
以此类推。
|
||||
|
||||
再看把物品1 放入背包:
|
||||
|
||||

|
||||
|
||||
背包容量为 0,放不下物品0 或者物品1,此时背包里的价值为0。
|
||||
|
||||
背包容量为 1,只能放下物品1,背包里的价值为15。
|
||||
|
||||
背包容量为 2,只能放下物品1,背包里的价值为15。
|
||||
|
||||
背包容量为 3,上一行同一状态,背包只能放物品0,这次也可以选择物品1了,背包可以放物品2 或者 物品1,物品2价值更大,背包里的价值为20。
|
||||
|
||||
背包容量为 4,上一行同一状态,背包只能放物品0,这次也可以选择物品1了,背包都可都放下,背包价值为35。
|
||||
|
||||
以上举例,是比较容易看懂,我主要是通过这个例子,来帮助大家明确dp数组的含义。
|
||||
|
||||
上图中,我们看 dp[1][4] 表示什么意思呢。
|
||||
|
||||
任取 物品0,物品1 放进容量为4的背包里,最大价值是 dp[1][4]。
|
||||
|
||||
通过这个举例,我们来进一步明确dp数组的含义。
|
||||
|
||||
即**dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少**。
|
||||
|
||||
**要时刻记着这个dp数组的含义,下面的一些步骤都围绕这dp数组的含义进行的**,如果哪里看懵了,就来回顾一下i代表什么,j又代表什么。
|
||||
|
||||
2. 确定递推公式
|
||||
|
||||
再回顾一下dp[i][j]的含义:从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
|
||||
这里在把基本信息给出来:
|
||||
|
||||
那么可以有两个方向推出来dp[i][j],
|
||||
| | 重量 | 价值 |
|
||||
| ----- | ---- | ---- |
|
||||
| 物品0 | 1 | 15 |
|
||||
| 物品1 | 3 | 20 |
|
||||
| 物品2 | 4 | 30 |
|
||||
|
||||
对于递推公式,首先我们要明确有哪些方向可以推导出 dp[i][j]。
|
||||
|
||||
这里我们dp[1][4]的状态来举例:
|
||||
|
||||
绝对 dp[1][4],就是放物品1 ,还是不放物品1。
|
||||
|
||||
如果不放物品1, 那么背包的价值应该是 dp[0][4] 即 容量为4的背包,只放物品0的情况。
|
||||
|
||||
推导方向如图:
|
||||
|
||||

|
||||
|
||||
|
||||
如果放物品1, **那么背包要先留出物品1的容量**,目前容量是4,物品1 需要重量为3,此时背包剩下容量为1。
|
||||
|
||||
容量为1,只考虑放物品0 的最大价值是 dp[0][1],这个值我们之前就计算过。
|
||||
|
||||
所以 放物品1 的情况 = dp[0][1] + 物品1 的重量,推导方向如图:
|
||||
|
||||

|
||||
|
||||
两种情况,分别是放物品1 和 不放物品1,我们要取最大值(毕竟求的是最大价值)
|
||||
|
||||
`dp[1][4] = max(dp[0][4], dp[0][1] + 物品1 的重量) `
|
||||
|
||||
以上过程,抽象化如下:
|
||||
|
||||
* **不放物品i**:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]。
|
||||
|
||||
* **不放物品i**:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面相同。)
|
||||
* **放物品i**:由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值
|
||||
|
||||
所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
递归公式: `dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);`
|
||||
|
||||
3. dp数组如何初始化
|
||||
|
||||
@ -108,13 +183,13 @@ leetcode上没有纯01背包的问题,都是01背包应用方面的题目,
|
||||
|
||||
在看其他情况。
|
||||
|
||||
状态转移方程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。
|
||||
状态转移方程 `dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);` 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。
|
||||
|
||||
dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
|
||||
|
||||
那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。
|
||||
那么很明显当 `j < weight[0]`的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。
|
||||
|
||||
当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。
|
||||
当`j >= weight[0]`时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。
|
||||
|
||||
代码初始化如下:
|
||||
|
||||
@ -200,7 +275,7 @@ for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
|
||||
|
||||
**要理解递归的本质和递推的方向**。
|
||||
|
||||
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 递归公式中可以看出dp[i][j]是靠dp[i-1][j]和dp[i - 1][j - weight[i]]推导出来的。
|
||||
`dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);` 递归公式中可以看出dp[i][j]是靠dp[i-1][j]和dp[i - 1][j - weight[i]]推导出来的。
|
||||
|
||||
dp[i-1][j]和dp[i - 1][j - weight[i]] 都在dp[i][j]的左上角方向(包括正上方向),那么先遍历物品,再遍历背包的过程如图所示:
|
||||
|
||||
@ -232,50 +307,20 @@ dp[i-1][j]和dp[i - 1][j - weight[i]] 都在dp[i][j]的左上角方向(包括
|
||||
|
||||
主要就是自己没有动手推导一下dp数组的演变过程,如果推导明白了,代码写出来就算有问题,只要把dp数组打印出来,对比一下和自己推导的有什么差异,很快就可以发现问题了。
|
||||
|
||||
```CPP
|
||||
void test_2_wei_bag_problem1() {
|
||||
vector<int> weight = {1, 3, 4};
|
||||
vector<int> value = {15, 20, 30};
|
||||
int bagweight = 4;
|
||||
|
||||
// 二维数组
|
||||
vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));
|
||||
|
||||
// 初始化
|
||||
for (int j = weight[0]; j <= bagweight; j++) {
|
||||
dp[0][j] = value[0];
|
||||
}
|
||||
|
||||
// weight数组的大小 就是物品个数
|
||||
for(int i = 1; i < weight.size(); i++) { // 遍历物品
|
||||
for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
|
||||
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
|
||||
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
cout << dp[weight.size() - 1][bagweight] << endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_2_wei_bag_problem1();
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
本题力扣上没有原题,大家可以去[卡码网第46题](https://kamacoder.com/problempage.php?pid=1046)去练习,题意是一样的,代码如下:
|
||||
|
||||
```CPP
|
||||
|
||||
//二维dp数组实现
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
int n, bagweight;// bagweight代表行李箱空间
|
||||
void solve() {
|
||||
int main() {
|
||||
int n, bagweight;// bagweight代表行李箱空间
|
||||
|
||||
cin >> n >> bagweight;
|
||||
|
||||
vector<int> weight(n, 0); // 存储每件物品所占空间
|
||||
vector<int> value(n, 0); // 存储每件物品价值
|
||||
|
||||
for(int i = 0; i < n; ++i) {
|
||||
cin >> weight[i];
|
||||
}
|
||||
@ -294,33 +339,28 @@ void solve() {
|
||||
|
||||
for(int i = 1; i < weight.size(); i++) { // 遍历科研物品
|
||||
for(int j = 0; j <= bagweight; j++) { // 遍历行李箱容量
|
||||
// 如果装不下这个物品,那么就继承dp[i - 1][j]的值
|
||||
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
|
||||
// 如果能装下,就将值更新为 不装这个物品的最大值 和 装这个物品的最大值 中的 最大值
|
||||
// 装这个物品的最大值由容量为j - weight[i]的包任意放入序号为[0, i - 1]的最大值 + 该物品的价值构成
|
||||
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
if (j < weight[i]) dp[i][j] = dp[i - 1][j]; // 如果装不下这个物品,那么就继承dp[i - 1][j]的值
|
||||
else {
|
||||
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
cout << dp[weight.size() - 1][bagweight] << endl;
|
||||
}
|
||||
cout << dp[n - 1][bagweight] << endl;
|
||||
|
||||
int main() {
|
||||
while(cin >> n >> bagweight) {
|
||||
solve();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 总结
|
||||
|
||||
讲了这么多才刚刚把二维dp的01背包讲完,**这里大家其实可以发现最简单的是推导公式了,推导公式估计看一遍就记下来了,但难就难在如何初始化和遍历顺序上**。
|
||||
背包问题 是动态规划里的经典类型题目,大家要细细品味。
|
||||
|
||||
可能有的同学并没有注意到初始化 和 遍历顺序的重要性,我们后面做力扣上背包面试题目的时候,大家就会感受出来了。
|
||||
|
||||
下一篇 还是理论基础,我们再来讲一维dp数组实现的01背包(滚动数组),分析一下和二维有什么区别,在初始化和遍历顺序上又有什么差异,敬请期待!
|
||||
下一篇 还是理论基础,我们再来讲一维dp数组实现的01背包(滚动数组),分析一下和二维有什么区别,在初始化和遍历顺序上又有什么差异。
|
||||
|
||||
|
||||
|
||||
@ -329,120 +369,42 @@ int main() {
|
||||
|
||||
### Java
|
||||
|
||||
```java
|
||||
public class BagProblem {
|
||||
```Java
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
int[] weight = {1,3,4};
|
||||
int[] value = {15,20,30};
|
||||
int bagSize = 4;
|
||||
testWeightBagProblem(weight,value,bagSize);
|
||||
}
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int n = scanner.nextInt();
|
||||
int bagweight = scanner.nextInt();
|
||||
|
||||
/**
|
||||
* 动态规划获得结果
|
||||
* @param weight 物品的重量
|
||||
* @param value 物品的价值
|
||||
* @param bagSize 背包的容量
|
||||
*/
|
||||
public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){
|
||||
int[] weight = new int[n];
|
||||
int[] value = new int[n];
|
||||
|
||||
// 创建dp数组
|
||||
int goods = weight.length; // 获取物品的数量
|
||||
int[][] dp = new int[goods][bagSize + 1];
|
||||
for (int i = 0; i < n; ++i) {
|
||||
weight[i] = scanner.nextInt();
|
||||
}
|
||||
for (int j = 0; j < n; ++j) {
|
||||
value[j] = scanner.nextInt();
|
||||
}
|
||||
|
||||
// 初始化dp数组
|
||||
// 创建数组后,其中默认的值就是0
|
||||
for (int j = weight[0]; j <= bagSize; j++) {
|
||||
int[][] dp = new int[n][bagweight + 1];
|
||||
|
||||
for (int j = weight[0]; j <= bagweight; j++) {
|
||||
dp[0][j] = value[0];
|
||||
}
|
||||
|
||||
// 填充dp数组
|
||||
for (int i = 1; i < weight.length; i++) {
|
||||
for (int j = 1; j <= bagSize; j++) {
|
||||
for (int i = 1; i < n; i++) {
|
||||
for (int j = 0; j <= bagweight; j++) {
|
||||
if (j < weight[i]) {
|
||||
/**
|
||||
* 当前背包的容量都没有当前物品i大的时候,是不放物品i的
|
||||
* 那么前i-1个物品能放下的最大价值就是当前情况的最大价值
|
||||
*/
|
||||
dp[i][j] = dp[i-1][j];
|
||||
} else {
|
||||
/**
|
||||
* 当前背包的容量可以放下物品i
|
||||
* 那么此时分两种情况:
|
||||
* 1、不放物品i
|
||||
* 2、放物品i
|
||||
* 比较这两种情况下,哪种背包中物品的最大价值最大
|
||||
*/
|
||||
dp[i][j] = Math.max(dp[i-1][j] , dp[i-1][j-weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 打印dp数组
|
||||
for (int i = 0; i < goods; i++) {
|
||||
for (int j = 0; j <= bagSize; j++) {
|
||||
System.out.print(dp[i][j] + "\t");
|
||||
}
|
||||
System.out.println("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
import java.util.Arrays;
|
||||
|
||||
public class BagProblem {
|
||||
public static void main(String[] args) {
|
||||
int[] weight = {1,3,4};
|
||||
int[] value = {15,20,30};
|
||||
int bagSize = 4;
|
||||
testWeightBagProblem(weight,value,bagSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 dp 数组做了简化(给物品增加冗余维)。这样初始化dp数组,默认全为0即可。
|
||||
* dp[i][j] 表示从下标为[0 - i-1]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
|
||||
* 其实是模仿背包重量从 0 开始,背包容量 j 为 0 的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为 0。
|
||||
* 可选物品也可以从无开始,也就是没有物品可选,即dp[0][j],这样无论背包容量为多少,背包价值总和一定为 0。
|
||||
* @param weight 物品的重量
|
||||
* @param value 物品的价值
|
||||
* @param bagSize 背包的容量
|
||||
*/
|
||||
public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){
|
||||
|
||||
// 创建dp数组
|
||||
int goods = weight.length; // 获取物品的数量
|
||||
int[][] dp = new int[goods + 1][bagSize + 1]; // 给物品增加冗余维,i = 0 表示没有物品可选
|
||||
|
||||
// 初始化dp数组,默认全为0即可
|
||||
// 填充dp数组
|
||||
for (int i = 1; i <= goods; i++) {
|
||||
for (int j = 1; j <= bagSize; j++) {
|
||||
if (j < weight[i - 1]) { // i - 1 对应物品 i
|
||||
/**
|
||||
* 当前背包的容量都没有当前物品i大的时候,是不放物品i的
|
||||
* 那么前i-1个物品能放下的最大价值就是当前情况的最大价值
|
||||
*/
|
||||
dp[i][j] = dp[i - 1][j];
|
||||
} else {
|
||||
/**
|
||||
* 当前背包的容量可以放下物品i
|
||||
* 那么此时分两种情况:
|
||||
* 1、不放物品i
|
||||
* 2、放物品i
|
||||
* 比较这两种情况下,哪种背包中物品的最大价值最大
|
||||
*/
|
||||
dp[i][j] = Math.max(dp[i - 1][j] , dp[i - 1][j - weight[i - 1]] + value[i - 1]); // i - 1 对应物品 i
|
||||
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 打印dp数组
|
||||
for(int[] arr : dp){
|
||||
System.out.println(Arrays.toString(arr));
|
||||
}
|
||||
System.out.println(dp[n - 1][bagweight]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -450,137 +412,71 @@ public class BagProblem {
|
||||
|
||||
### Python
|
||||
|
||||
无参数版
|
||||
```python
|
||||
def test_2_wei_bag_problem1():
|
||||
weight = [1, 3, 4]
|
||||
value = [15, 20, 30]
|
||||
bagweight = 4
|
||||
n, bagweight = map(int, input().split())
|
||||
|
||||
# 二维数组
|
||||
dp = [[0] * (bagweight + 1) for _ in range(len(weight))]
|
||||
weight = list(map(int, input().split()))
|
||||
value = list(map(int, input().split()))
|
||||
|
||||
# 初始化
|
||||
for j in range(weight[0], bagweight + 1):
|
||||
dp[0][j] = value[0]
|
||||
dp = [[0] * (bagweight + 1) for _ in range(n)]
|
||||
|
||||
# weight数组的大小就是物品个数
|
||||
for i in range(1, len(weight)): # 遍历物品
|
||||
for j in range(bagweight + 1): # 遍历背包容量
|
||||
if j < weight[i]:
|
||||
dp[i][j] = dp[i - 1][j]
|
||||
else:
|
||||
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
|
||||
for j in range(weight[0], bagweight + 1):
|
||||
dp[0][j] = value[0]
|
||||
|
||||
print(dp[len(weight) - 1][bagweight])
|
||||
|
||||
test_2_wei_bag_problem1()
|
||||
|
||||
```
|
||||
有参数版
|
||||
```python
|
||||
def test_2_wei_bag_problem1(weight, value, bagweight):
|
||||
# 二维数组
|
||||
dp = [[0] * (bagweight + 1) for _ in range(len(weight))]
|
||||
|
||||
# 初始化
|
||||
for j in range(weight[0], bagweight + 1):
|
||||
dp[0][j] = value[0]
|
||||
|
||||
# weight数组的大小就是物品个数
|
||||
for i in range(1, len(weight)): # 遍历物品
|
||||
for j in range(bagweight + 1): # 遍历背包容量
|
||||
if j < weight[i]:
|
||||
dp[i][j] = dp[i - 1][j]
|
||||
else:
|
||||
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
|
||||
|
||||
return dp[len(weight) - 1][bagweight]
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
weight = [1, 3, 4]
|
||||
value = [15, 20, 30]
|
||||
bagweight = 4
|
||||
|
||||
result = test_2_wei_bag_problem1(weight, value, bagweight)
|
||||
print(result)
|
||||
for i in range(1, n):
|
||||
for j in range(bagweight + 1):
|
||||
if j < weight[i]:
|
||||
dp[i][j] = dp[i - 1][j]
|
||||
else:
|
||||
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
|
||||
|
||||
print(dp[n - 1][bagweight])
|
||||
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
```go
|
||||
func test_2_wei_bag_problem1(weight, value []int, bagweight int) int {
|
||||
// 定义dp数组
|
||||
dp := make([][]int, len(weight))
|
||||
for i, _ := range dp {
|
||||
dp[i] = make([]int, bagweight+1)
|
||||
}
|
||||
// 初始化
|
||||
for j := bagweight; j >= weight[0]; j-- {
|
||||
dp[0][j] = dp[0][j-weight[0]] + value[0]
|
||||
}
|
||||
// 递推公式
|
||||
for i := 1; i < len(weight); i++ {
|
||||
//正序,也可以倒序
|
||||
for j := 0; j <= bagweight; j++ {
|
||||
if j < weight[i] {
|
||||
dp[i][j] = dp[i-1][j]
|
||||
} else {
|
||||
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[len(weight)-1][bagweight]
|
||||
}
|
||||
|
||||
func max(a,b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func main() {
|
||||
weight := []int{1,3,4}
|
||||
value := []int{15,20,30}
|
||||
test_2_wei_bag_problem1(weight,value,4)
|
||||
}
|
||||
```
|
||||
|
||||
### Javascript
|
||||
|
||||
```js
|
||||
function testWeightBagProblem (weight, value, size) {
|
||||
// 定义 dp 数组
|
||||
const len = weight.length,
|
||||
dp = Array(len).fill().map(() => Array(size + 1).fill(0));
|
||||
const readline = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
// 初始化
|
||||
for(let j = weight[0]; j <= size; j++) {
|
||||
let input = [];
|
||||
|
||||
readline.on('line', (line) => {
|
||||
input.push(line);
|
||||
});
|
||||
|
||||
readline.on('close', () => {
|
||||
let [n, bagweight] = input[0].split(' ').map(Number);
|
||||
let weight = input[1].split(' ').map(Number);
|
||||
let value = input[2].split(' ').map(Number);
|
||||
|
||||
let dp = Array.from({ length: n }, () => Array(bagweight + 1).fill(0));
|
||||
|
||||
for (let j = weight[0]; j <= bagweight; j++) {
|
||||
dp[0][j] = value[0];
|
||||
}
|
||||
|
||||
// weight 数组的长度len 就是物品个数
|
||||
for(let i = 1; i < len; i++) { // 遍历物品
|
||||
for(let j = 0; j <= size; j++) { // 遍历背包容量
|
||||
if(j < weight[i]) dp[i][j] = dp[i - 1][j];
|
||||
else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
for (let i = 1; i < n; i++) {
|
||||
for (let j = 0; j <= bagweight; j++) {
|
||||
if (j < weight[i]) {
|
||||
dp[i][j] = dp[i - 1][j];
|
||||
} else {
|
||||
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.table(dp)
|
||||
console.log(dp[n - 1][bagweight]);
|
||||
});
|
||||
|
||||
return dp[len - 1][size];
|
||||
}
|
||||
|
||||
function test () {
|
||||
console.log(testWeightBagProblem([1, 3, 4, 5], [15, 20, 30, 55], 6));
|
||||
}
|
||||
|
||||
test();
|
||||
```
|
||||
|
||||
|
||||
@ -589,158 +485,63 @@ test();
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define ARR_SIZE(a) (sizeof((a)) / sizeof((a)[0]))
|
||||
#define BAG_WEIGHT 4
|
||||
int max(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
void backPack(int* weights, int weightSize, int* costs, int costSize, int bagWeight) {
|
||||
// 开辟dp数组
|
||||
int dp[weightSize][bagWeight + 1];
|
||||
memset(dp, 0, sizeof(int) * weightSize * (bagWeight + 1));
|
||||
int main() {
|
||||
int n, bagweight;
|
||||
scanf("%d %d", &n, &bagweight);
|
||||
|
||||
int i, j;
|
||||
// 当背包容量大于物品0的重量时,将物品0放入到背包中
|
||||
for(j = weights[0]; j <= bagWeight; ++j) {
|
||||
dp[0][j] = costs[0];
|
||||
int *weight = (int *)malloc(n * sizeof(int));
|
||||
int *value = (int *)malloc(n * sizeof(int));
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
scanf("%d", &weight[i]);
|
||||
}
|
||||
for (int j = 0; j < n; ++j) {
|
||||
scanf("%d", &value[j]);
|
||||
}
|
||||
|
||||
// 先遍历物品,再遍历重量
|
||||
for(j = 1; j <= bagWeight; ++j) {
|
||||
for(i = 1; i < weightSize; ++i) {
|
||||
// 如果当前背包容量小于物品重量
|
||||
if(j < weights[i])
|
||||
// 背包物品的价值等于背包不放置当前物品时的价值
|
||||
dp[i][j] = dp[i-1][j];
|
||||
// 若背包当前重量可以放置物品
|
||||
else
|
||||
// 背包的价值等于放置该物品或不放置该物品的最大值
|
||||
dp[i][j] = MAX(dp[i - 1][j], dp[i - 1][j - weights[i]] + costs[i]);
|
||||
int **dp = (int **)malloc(n * sizeof(int *));
|
||||
for (int i = 0; i < n; ++i) {
|
||||
dp[i] = (int *)malloc((bagweight + 1) * sizeof(int));
|
||||
for (int j = 0; j <= bagweight; ++j) {
|
||||
dp[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%d\n", dp[weightSize - 1][bagWeight]);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int weights[] = {1, 3, 4};
|
||||
int costs[] = {15, 20, 30};
|
||||
backPack(weights, ARR_SIZE(weights), costs, ARR_SIZE(costs), BAG_WEIGHT);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function testWeightBagProblem(
|
||||
weight: number[],
|
||||
value: number[],
|
||||
size: number
|
||||
): number {
|
||||
/**
|
||||
* dp[i][j]: 前i个物品,背包容量为j,能获得的最大价值
|
||||
* dp[0][*]: u=weight[0],u之前为0,u之后(含u)为value[0]
|
||||
* dp[*][0]: 0
|
||||
* ...
|
||||
* dp[i][j]: max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i]);
|
||||
*/
|
||||
const goodsNum: number = weight.length;
|
||||
const dp: number[][] = new Array(goodsNum)
|
||||
.fill(0)
|
||||
.map((_) => new Array(size + 1).fill(0));
|
||||
for (let i = weight[0]; i <= size; i++) {
|
||||
dp[0][i] = value[0];
|
||||
}
|
||||
for (let i = 1; i < goodsNum; i++) {
|
||||
for (let j = 1; j <= size; j++) {
|
||||
if (j < weight[i]) {
|
||||
dp[i][j] = dp[i - 1][j];
|
||||
} else {
|
||||
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[goodsNum - 1][size];
|
||||
}
|
||||
// test
|
||||
const weight = [1, 3, 4];
|
||||
const value = [15, 20, 30];
|
||||
const size = 4;
|
||||
console.log(testWeightBagProblem(weight, value, size));
|
||||
```
|
||||
|
||||
### Scala
|
||||
|
||||
```scala
|
||||
object Solution {
|
||||
// 01背包
|
||||
def test_2_wei_bag_problem1(): Unit = {
|
||||
var weight = Array[Int](1, 3, 4)
|
||||
var value = Array[Int](15, 20, 30)
|
||||
var baseweight = 4
|
||||
|
||||
// 二维数组
|
||||
var dp = Array.ofDim[Int](weight.length, baseweight + 1)
|
||||
|
||||
// 初始化
|
||||
for (j <- weight(0) to baseweight) {
|
||||
dp(0)(j) = value(0)
|
||||
for (int j = weight[0]; j <= bagweight; j++) {
|
||||
dp[0][j] = value[0];
|
||||
}
|
||||
|
||||
// 遍历
|
||||
for (i <- 1 until weight.length; j <- 1 to baseweight) {
|
||||
if (j - weight(i) >= 0) dp(i)(j) = dp(i - 1)(j - weight(i)) + value(i)
|
||||
dp(i)(j) = math.max(dp(i)(j), dp(i - 1)(j))
|
||||
}
|
||||
|
||||
// 打印数组
|
||||
dp.foreach(x => println("[" + x.mkString(",") + "]"))
|
||||
|
||||
dp(weight.length - 1)(baseweight) // 最终返回
|
||||
}
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
test_2_wei_bag_problem1()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Rust
|
||||
|
||||
```rust
|
||||
pub struct Solution;
|
||||
|
||||
impl Solution {
|
||||
pub fn wei_bag_problem1(weight: Vec<usize>, value: Vec<usize>, bag_size: usize) -> usize {
|
||||
let mut dp = vec![vec![0; bag_size + 1]; weight.len()];
|
||||
for j in weight[0]..=weight.len() {
|
||||
dp[0][j] = value[0];
|
||||
}
|
||||
|
||||
for i in 1..weight.len() {
|
||||
for j in 0..=bag_size {
|
||||
match j < weight[i] {
|
||||
true => dp[i][j] = dp[i - 1][j],
|
||||
false => dp[i][j] = dp[i - 1][j].max(dp[i - 1][j - weight[i]] + value[i]),
|
||||
}
|
||||
for (int i = 1; i < n; i++) {
|
||||
for (int j = 0; j <= bagweight; j++) {
|
||||
if (j < weight[i]) {
|
||||
dp[i][j] = dp[i - 1][j];
|
||||
} else {
|
||||
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
dp[weight.len() - 1][bag_size]
|
||||
}
|
||||
|
||||
printf("%d\n", dp[n - 1][bagweight]);
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
free(dp[i]);
|
||||
}
|
||||
free(dp);
|
||||
free(weight);
|
||||
free(value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wei_bag_problem1() {
|
||||
println!(
|
||||
"{}",
|
||||
Solution::wei_bag_problem1(vec![1, 3, 4], vec![15, 20, 30], 4)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
|
||||
|
@ -56,17 +56,31 @@
|
||||
|
||||
1. 确定dp数组的定义
|
||||
|
||||
关于dp数组的定义,我在 [01背包理论基础](https://programmercarl.com/背包理论基础01背包-1.html) 有详细讲解
|
||||
|
||||
在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。
|
||||
|
||||
2. 一维dp数组的递推公式
|
||||
|
||||
dp[j]为 容量为j的背包所背的最大价值,那么如何推导dp[j]呢?
|
||||
二维dp数组的递推公式为: `dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);`
|
||||
|
||||
公式是怎么来的 在这里 [01背包理论基础](https://programmercarl.com/背包理论基础01背包-1.html) 有详细讲解。
|
||||
|
||||
一维dp数组,其实就上上一层 dp[i-1] 这一层 拷贝的 dp[i]来。
|
||||
|
||||
所以在 上面递推公式的基础上,去掉i这个维度就好。
|
||||
|
||||
递推公式为:`dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);`
|
||||
|
||||
以下为分析:
|
||||
|
||||
dp[j]为 容量为j的背包所背的最大价值。
|
||||
|
||||
dp[j]可以通过dp[j - weight[i]]推导出来,dp[j - weight[i]]表示容量为j - weight[i]的背包所背的最大价值。
|
||||
|
||||
dp[j - weight[i]] + value[i] 表示 容量为 j - 物品i重量 的背包 加上 物品i的价值。(也就是容量为j的背包,放入物品i了之后的价值即:dp[j])
|
||||
`dp[j - weight[i]] + value[i]` 表示 容量为 [j - 物品i重量] 的背包 加上 物品i的价值。(也就是容量为j的背包,放入物品i了之后的价值即:dp[j])
|
||||
|
||||
此时dp[j]有两个选择,一个是取自己dp[j] 相当于 二维dp数组中的dp[i-1][j],即不放物品i,一个是取dp[j - weight[i]] + value[i],即放物品i,指定是取最大的,毕竟是求最大价值,
|
||||
此时dp[j]有两个选择,一个是取自己dp[j] 相当于 二维dp数组中的dp[i-1][j],即不放物品i,一个是取`dp[j - weight[i]] + value[i]`,即放物品i,指定是取最大的,毕竟是求最大价值,
|
||||
|
||||
所以递归公式为:
|
||||
|
||||
@ -145,10 +159,6 @@ dp[1] = dp[1 - weight[0]] + value[0] = 15
|
||||
|
||||
因为一维dp的写法,背包容量一定是要倒序遍历(原因上面已经讲了),如果遍历背包容量放在上一层,那么每个dp[j]就只会放入一个物品,即:背包里只放入了一个物品。
|
||||
|
||||
倒序遍历的原因是,本质上还是一个对二维数组的遍历,并且右下角的值依赖上一层左上角的值,因此需要保证左边的值仍然是上一层的,从右向左覆盖。
|
||||
|
||||
(这里如果读不懂,就再回想一下dp[j]的定义,或者就把两个for循环顺序颠倒一下试试!)
|
||||
|
||||
**所以一维dp数组的背包在遍历顺序上和二维其实是有很大差异的!**,这一点大家一定要注意。
|
||||
|
||||
5. 举例推导dp数组
|
||||
@ -158,31 +168,6 @@ dp[1] = dp[1 - weight[0]] + value[0] = 15
|
||||

|
||||
|
||||
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```CPP
|
||||
void test_1_wei_bag_problem() {
|
||||
vector<int> weight = {1, 3, 4};
|
||||
vector<int> value = {15, 20, 30};
|
||||
int bagWeight = 4;
|
||||
|
||||
// 初始化
|
||||
vector<int> dp(bagWeight + 1, 0);
|
||||
for(int i = 0; i < weight.size(); i++) { // 遍历物品
|
||||
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
|
||||
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
cout << dp[bagWeight] << endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_1_wei_bag_problem();
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
本题力扣上没有原题,大家可以去[卡码网第46题](https://kamacoder.com/problempage.php?pid=1046)去练习,题意是一样的,代码如下:
|
||||
|
||||
```CPP
|
||||
@ -256,251 +241,229 @@ int main() {
|
||||
|
||||
即使代码没有通过,也会有自己的逻辑去debug,这样就思维清晰了。
|
||||
|
||||
接下来就要开始用这两天的理论基础去做力扣上的背包面试题目了,录友们握紧扶手,我们要上高速啦!
|
||||
|
||||
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
### Java
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
int[] weight = {1, 3, 4};
|
||||
int[] value = {15, 20, 30};
|
||||
int bagWight = 4;
|
||||
testWeightBagProblem(weight, value, bagWight);
|
||||
}
|
||||
import java.util.Scanner;
|
||||
|
||||
public static void testWeightBagProblem(int[] weight, int[] value, int bagWeight){
|
||||
int wLen = weight.length;
|
||||
//定义dp数组:dp[j]表示背包容量为j时,能获得的最大价值
|
||||
int[] dp = new int[bagWeight + 1];
|
||||
//遍历顺序:先遍历物品,再遍历背包容量
|
||||
for (int i = 0; i < wLen; i++){
|
||||
for (int j = bagWeight; j >= weight[i]; j--){
|
||||
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int n = scanner.nextInt();
|
||||
int bagweight = scanner.nextInt();
|
||||
|
||||
int[] weight = new int[n];
|
||||
int[] value = new int[n];
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
weight[i] = scanner.nextInt();
|
||||
}
|
||||
for (int j = 0; j < n; ++j) {
|
||||
value[j] = scanner.nextInt();
|
||||
}
|
||||
|
||||
int[][] dp = new int[n][bagweight + 1];
|
||||
|
||||
for (int j = weight[0]; j <= bagweight; j++) {
|
||||
dp[0][j] = value[0];
|
||||
}
|
||||
|
||||
for (int i = 1; i < n; i++) {
|
||||
for (int j = 0; j <= bagweight; j++) {
|
||||
if (j < weight[i]) {
|
||||
dp[i][j] = dp[i - 1][j];
|
||||
} else {
|
||||
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//打印dp数组
|
||||
for (int j = 0; j <= bagWeight; j++){
|
||||
System.out.print(dp[j] + " ");
|
||||
}
|
||||
|
||||
System.out.println(dp[n - 1][bagweight]);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### Python
|
||||
无参版
|
||||
|
||||
```python
|
||||
def test_1_wei_bag_problem():
|
||||
weight = [1, 3, 4]
|
||||
value = [15, 20, 30]
|
||||
bagWeight = 4
|
||||
n, bagweight = map(int, input().split())
|
||||
|
||||
# 初始化
|
||||
dp = [0] * (bagWeight + 1)
|
||||
for i in range(len(weight)): # 遍历物品
|
||||
for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量
|
||||
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
|
||||
weight = list(map(int, input().split()))
|
||||
value = list(map(int, input().split()))
|
||||
|
||||
print(dp[bagWeight])
|
||||
dp = [[0] * (bagweight + 1) for _ in range(n)]
|
||||
|
||||
for j in range(weight[0], bagweight + 1):
|
||||
dp[0][j] = value[0]
|
||||
|
||||
test_1_wei_bag_problem()
|
||||
```
|
||||
有参版
|
||||
```python
|
||||
def test_1_wei_bag_problem(weight, value, bagWeight):
|
||||
# 初始化
|
||||
dp = [0] * (bagWeight + 1)
|
||||
for i in range(len(weight)): # 遍历物品
|
||||
for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量
|
||||
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
|
||||
for i in range(1, n):
|
||||
for j in range(bagweight + 1):
|
||||
if j < weight[i]:
|
||||
dp[i][j] = dp[i - 1][j]
|
||||
else:
|
||||
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
|
||||
|
||||
return dp[bagWeight]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
weight = [1, 3, 4]
|
||||
value = [15, 20, 30]
|
||||
bagweight = 4
|
||||
|
||||
result = test_1_wei_bag_problem(weight, value, bagweight)
|
||||
print(result)
|
||||
print(dp[n - 1][bagweight])
|
||||
|
||||
```
|
||||
### Go
|
||||
```go
|
||||
func test_1_wei_bag_problem(weight, value []int, bagWeight int) int {
|
||||
// 定义 and 初始化
|
||||
dp := make([]int,bagWeight+1)
|
||||
// 递推顺序
|
||||
for i := 0 ;i < len(weight) ; i++ {
|
||||
// 这里必须倒序,区别二维,因为二维dp保存了i的状态
|
||||
for j:= bagWeight; j >= weight[i] ; j-- {
|
||||
// 递推公式
|
||||
dp[j] = max(dp[j], dp[j-weight[i]]+value[i])
|
||||
}
|
||||
}
|
||||
//fmt.Println(dp)
|
||||
return dp[bagWeight]
|
||||
}
|
||||
|
||||
func max(a,b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
weight := []int{1,3,4}
|
||||
value := []int{15,20,30}
|
||||
test_1_wei_bag_problem(weight,value,4)
|
||||
var n, bagweight int
|
||||
fmt.Scan(&n, &bagweight)
|
||||
|
||||
weight := make([]int, n)
|
||||
value := make([]int, n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
fmt.Scan(&weight[i])
|
||||
}
|
||||
for j := 0; j < n; j++ {
|
||||
fmt.Scan(&value[j])
|
||||
}
|
||||
|
||||
dp := make([][]int, n)
|
||||
for i := range dp {
|
||||
dp[i] = make([]int, bagweight+1)
|
||||
}
|
||||
|
||||
for j := weight[0]; j <= bagweight; j++ {
|
||||
dp[0][j] = value[0]
|
||||
}
|
||||
|
||||
for i := 1; i < n; i++ {
|
||||
for j := 0; j <= bagweight; j++ {
|
||||
if j < weight[i] {
|
||||
dp[i][j] = dp[i-1][j]
|
||||
} else {
|
||||
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(dp[n-1][bagweight])
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
|
||||
```js
|
||||
const readline = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
function testWeightBagProblem(wight, value, size) {
|
||||
const len = wight.length,
|
||||
dp = Array(size + 1).fill(0);
|
||||
for(let i = 1; i <= len; i++) {
|
||||
for(let j = size; j >= wight[i - 1]; j--) {
|
||||
dp[j] = Math.max(dp[j], value[i - 1] + dp[j - wight[i - 1]]);
|
||||
let input = [];
|
||||
|
||||
readline.on('line', (line) => {
|
||||
input.push(line);
|
||||
});
|
||||
|
||||
readline.on('close', () => {
|
||||
let [n, bagweight] = input[0].split(' ').map(Number);
|
||||
let weight = input[1].split(' ').map(Number);
|
||||
let value = input[2].split(' ').map(Number);
|
||||
|
||||
let dp = Array.from({ length: n }, () => Array(bagweight + 1).fill(0));
|
||||
|
||||
for (let j = weight[0]; j <= bagweight; j++) {
|
||||
dp[0][j] = value[0];
|
||||
}
|
||||
}
|
||||
return dp[size];
|
||||
}
|
||||
|
||||
for (let i = 1; i < n; i++) {
|
||||
for (let j = 0; j <= bagweight; j++) {
|
||||
if (j < weight[i]) {
|
||||
dp[i][j] = dp[i - 1][j];
|
||||
} else {
|
||||
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(dp[n - 1][bagweight]);
|
||||
});
|
||||
|
||||
|
||||
function test () {
|
||||
console.log(testWeightBagProblem([1, 3, 4, 5], [15, 20, 30, 55], 6));
|
||||
}
|
||||
|
||||
test();
|
||||
```
|
||||
|
||||
### C
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define ARR_SIZE(arr) ((sizeof((arr))) / sizeof((arr)[0]))
|
||||
#define BAG_WEIGHT 4
|
||||
int max(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
void test_back_pack(int* weights, int weightSize, int* values, int valueSize, int bagWeight) {
|
||||
int dp[bagWeight + 1];
|
||||
memset(dp, 0, sizeof(int) * (bagWeight + 1));
|
||||
int main() {
|
||||
int n, bagweight;
|
||||
scanf("%d %d", &n, &bagweight);
|
||||
|
||||
int i, j;
|
||||
// 先遍历物品
|
||||
for(i = 0; i < weightSize; ++i) {
|
||||
// 后遍历重量。从后向前遍历
|
||||
for(j = bagWeight; j >= weights[i]; --j) {
|
||||
dp[j] = MAX(dp[j], dp[j - weights[i]] + values[i]);
|
||||
int *weight = (int *)malloc(n * sizeof(int));
|
||||
int *value = (int *)malloc(n * sizeof(int));
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
scanf("%d", &weight[i]);
|
||||
}
|
||||
for (int j = 0; j < n; ++j) {
|
||||
scanf("%d", &value[j]);
|
||||
}
|
||||
|
||||
int **dp = (int **)malloc(n * sizeof(int *));
|
||||
for (int i = 0; i < n; ++i) {
|
||||
dp[i] = (int *)malloc((bagweight + 1) * sizeof(int));
|
||||
for (int j = 0; j <= bagweight; ++j) {
|
||||
dp[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 打印最优结果
|
||||
printf("%d\n", dp[bagWeight]);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int weights[] = {1, 3, 4};
|
||||
int values[] = {15, 20, 30};
|
||||
test_back_pack(weights, ARR_SIZE(weights), values, ARR_SIZE(values), BAG_WEIGHT);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function testWeightBagProblem(
|
||||
weight: number[],
|
||||
value: number[],
|
||||
size: number
|
||||
): number {
|
||||
const goodsNum: number = weight.length;
|
||||
const dp: number[] = new Array(size + 1).fill(0);
|
||||
for (let i = 0; i < goodsNum; i++) {
|
||||
for (let j = size; j >= weight[i]; j--) {
|
||||
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
return dp[size];
|
||||
}
|
||||
const weight = [1, 3, 4];
|
||||
const value = [15, 20, 30];
|
||||
const size = 4;
|
||||
console.log(testWeightBagProblem(weight, value, size));
|
||||
|
||||
```
|
||||
|
||||
### Scala
|
||||
|
||||
```scala
|
||||
object Solution {
|
||||
// 滚动数组
|
||||
def test_1_wei_bag_problem(): Unit = {
|
||||
var weight = Array[Int](1, 3, 4)
|
||||
var value = Array[Int](15, 20, 30)
|
||||
var baseweight = 4
|
||||
|
||||
// dp数组
|
||||
var dp = new Array[Int](baseweight + 1)
|
||||
|
||||
// 遍历
|
||||
for (i <- 0 until weight.length; j <- baseweight to weight(i) by -1) {
|
||||
dp(j) = math.max(dp(j), dp(j - weight(i)) + value(i))
|
||||
for (int j = weight[0]; j <= bagweight; j++) {
|
||||
dp[0][j] = value[0];
|
||||
}
|
||||
|
||||
// 打印数组
|
||||
println("[" + dp.mkString(",") + "]")
|
||||
}
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
test_1_wei_bag_problem()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Rust
|
||||
|
||||
```rust
|
||||
pub struct Solution;
|
||||
|
||||
impl Solution {
|
||||
pub fn wei_bag_problem2(weight: Vec<usize>, value: Vec<usize>, bag_size: usize) -> usize {
|
||||
let mut dp = vec![0; bag_size + 1];
|
||||
for i in 0..weight.len() {
|
||||
for j in (weight[i]..=bag_size).rev() {
|
||||
if j >= weight[i] {
|
||||
dp[j] = dp[j].max(dp[j - weight[i]] + value[i]);
|
||||
}
|
||||
for (int i = 1; i < n; i++) {
|
||||
for (int j = 0; j <= bagweight; j++) {
|
||||
if (j < weight[i]) {
|
||||
dp[i][j] = dp[i - 1][j];
|
||||
} else {
|
||||
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
dp[dp.len() - 1]
|
||||
}
|
||||
|
||||
printf("%d\n", dp[n - 1][bagweight]);
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
free(dp[i]);
|
||||
}
|
||||
free(dp);
|
||||
free(weight);
|
||||
free(value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wei_bag_problem2() {
|
||||
println!(
|
||||
"{}",
|
||||
Solution::wei_bag_problem2(vec![1, 3, 4], vec![15, 20, 30], 4)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user