力扣刷题之3098.求出所有子序列的能量和

题干描述

给你一个长度为 n 的整数数组 nums 和一个  整数 k 。

一个 

子序列

 的 能量 定义为子序列中 任意 两个元素的差值绝对值的 最小值 。

请你返回 nums 中长度 等于 k 的 所有 子序列的 能量和 。

由于答案可能会很大,将答案对 109 + 7 取余 后返回。

示例 1:

输入:nums = [1,2,3,4], k = 3

输出:4

解释:

nums 中总共有 4 个长度为 3 的子序列:[1,2,3] ,[1,3,4] ,[1,2,4] 和 [2,3,4] 。能量和为 |2 - 3| + |3 - 4| + |2 - 1| + |3 - 4| = 4 。

示例 2:

输入:nums = [2,2], k = 2

输出:0

解释:

nums 中唯一一个长度为 2 的子序列是 [2,2] 。能量和为 |2 - 2| = 0 。

示例 3:

输入:nums = [4,3,-1], k = 2

输出:10

解释:

nums 总共有 3 个长度为 2 的子序列:[4,3] ,[4,-1] 和 [3,-1] 。能量和为 |4 - 3| + |4 - (-1)| + |3 - (-1)| = 10 。

题干解析

         首先我们先要理解题干的相关需求:我们需要计算所有长度为K的子序列的能量和,并返回结果对109+7 取余。

解题思路

1.理解题目要求
  • 给定一个长度为n的整数数组nums和一个正整数k。
  • 目标是找到所有长度为k的子序列,并计算每个子序列的能量。
  • 子序列的能量定义是为子序列中任意来个元素的长治绝对值的最小值。
  • 最后返回所有子序列的能量和。
2.动态规划和哈希表的结合
  • 为了有效地解决问题,我们采用动态规划和哈希表结合的方法。这种方法的关键点是:
  • 动态规划 用于记录不同长度子序列的能量。
  • 哈希表 用于存储子序列的能量技术,确保快速查找和更新。
3.初始化数据结构
  • 动态分配一个三维数组d来存储哈希表。
  • 每个d[i][j]表示以nums[i]结尾的长度为j的子序列及其能量信息。
4.对数组进行排序
  • 使用快速排序对nums进行排序,以便于后续计算两个元素的差值。
5.主要是动态规划过程
  • 遍历数组nums中的每个元素nums[i]。
  • 初始化d[i][1],即长度为1的子序列,其能量为INF(即无穷大),计数为1。
hashAddItem(&d[i][1], INF, 1);
  •  对于每个nums[i],遍历之前的所有元素nums[j]。
for (int j = 0; j < i; j++) {
    int diff = abs(nums[i] - nums[j]);
  • 对于每个可能的长度p(从2到k),计算从nums[i]结尾的长度为p的子序列的能量。
for (int p = 2; p <= k; p++) {
    for (HashItem *pEntry = d[j][p - 1]; pEntry; pEntry = pEntry->hh.next) {
        int v = pEntry->key, cnt = pEntry->val;
        int key = (diff < v) ? diff : v; // 计算最小能量
        hashSetItem(&d[i][p], key, (hashGetItem(&d[i][p], key, 0) + cnt) % MOD);
    }
}
  • 更新d[i][p]中以nums[i]结尾的长度为p的子序列的能量和技术。
6.计算总能量和
  • 遍历所有长度为k的子序列,计算它们的能量和。
for (HashItem *pEntry = d[i][k]; pEntry; pEntry = pEntry->hh.next) {
    int v = pEntry->key, cnt = pEntry->val;
    res = (res + 1ll * v * cnt % MOD) % MOD;
}
  • 使用% MOD确保结果在范围内。
7.释放内存
  • 释放动态分配的内存,防止内存泄漏
for (int i = 0; i < n; i++) {
    for (int j = 0; j <= k; j++) {
        hashFree(&d[i][j]);
    }
    free(d[i]);
}
free(d);

总结

1.初始化数据结构

  • 动态分配一个三维数组d来存储哈希表。
  • 初始化每个哈希表NULL。

2.对数组排序

  • 使用快速排序对nums进行排序。

3.动态规划填表

对于每个元素nums[i]:

  • 初始化d[i][j]
  • 对于每个j < i,计算nums[i]和nums[j]之间的差值diff。
  • 对于每个p(从2到k)更新长度为p的子序列的能量和计数。

4.计算总能量和

  • 遍历所有长度为k子序列,计算它们的能量和。
  • 使用% MOD确保结果在范围内。
5.释放所有动态分配的内存,防止内存泄漏。

完整代码如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "uthash.h"

// 定义哈希表项结构
typedef struct {
    int key;
    int val;
    UT_hash_handle hh;
} HashItem;

// 查找哈希表中的项
HashItem *hashFindItem(HashItem **obj, int key) {
    HashItem *pEntry = NULL;
    HASH_FIND_INT(*obj, &key, pEntry);
    return pEntry;
}

// 向哈希表中添加项
bool hashAddItem(HashItem **obj, int key, int val) {
    if (hashFindItem(obj, key)) {
        return false;
    }
    HashItem *pEntry = (HashItem *)malloc(sizeof(HashItem));
    pEntry->key = key;
    pEntry->val = val;
    HASH_ADD_INT(*obj, key, pEntry);
    return true;
}

// 设置哈希表中的项
bool hashSetItem(HashItem **obj, int key, int val) {
    HashItem *pEntry = hashFindItem(obj, key);
    if (!pEntry) {
        hashAddItem(obj, key, val);
    } else {
        pEntry->val = val;
    }
    return true;
}

// 获取哈希表中的项
int hashGetItem(HashItem **obj, int key, int defaultVal) {
    HashItem *pEntry = hashFindItem(obj, key);
    if (!pEntry) {
        return defaultVal;
    }
    return pEntry->val;
}

// 释放哈希表
void hashFree(HashItem **obj) {
    HashItem *curr = NULL, *tmp = NULL;
    HASH_ITER(hh, *obj, curr, tmp) {
        HASH_DEL(*obj, curr);
        free(curr);
    }
}

// 定义一些常量
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;

// 比较函数,用于qsort排序
static int cmp(const void *a, const void *b) {
    return *(int *)a - *(int *)b;
}

// 计算幂和的函数
int sumOfPowers(int* nums, int numsSize, int k) {
    int n = numsSize;
    int res = 0;

    // 动态分配内存
    HashItem ****d = (HashItem ****)malloc(n * sizeof(HashItem ***));  // 修改1
    for (int i = 0; i < n; i++) {
        d[i] = (HashItem ***)malloc((k + 1) * sizeof(HashItem **));    // 修改2
        for (int j = 0; j <= k; j++) {
            d[i][j] = NULL;
        }
    }

    // 对数组进行排序
    qsort(nums, numsSize, sizeof(int), cmp);

    // 主要的动态规划过程
    for (int i = 0; i < n; i++) {
        hashAddItem(&d[i][1], INF, 1); // 修改3
        for (int j = 0; j < i; j++) {
            int diff = abs(nums[i] - nums[j]);
            for (int p = 2; p <= k; p++) {
                for (HashItem *pEntry = d[j][p - 1]; pEntry; pEntry = pEntry->hh.next) { // 修改4
                    int v = pEntry->key, cnt = pEntry->val;
                    int key = (diff < v) ? diff : v; // 替换fmin函数
                    hashSetItem(&d[i][p], key, (hashGetItem(&d[i][p], key, 0) + cnt) % MOD); // 修改5
                }
            }
        }
        for (HashItem *pEntry = d[i][k]; pEntry; pEntry = pEntry->hh.next) {
            int v = pEntry->key, cnt = pEntry->val;
            res = (res + 1ll * v * cnt % MOD) % MOD;
        }
    }

    // 释放动态分配的内存
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= k; j++) {
            hashFree(&d[i][j]);
        }
        free(d[i]);  // 修改6
    }
    free(d);  // 修改7

    return res;
}

int main() {
    int nums[] = {1, 2, 3, 4};
    int numsSize = sizeof(nums) / sizeof(nums[0]);
    int k = 3;
    int result = sumOfPowers(nums, numsSize, k);
    printf("结果: %d\n", result); // 输出结果: 4

    int nums2[] = {2, 2};
    int numsSize2 = sizeof(nums2) / sizeof(nums2[0]);
    k = 2;
    result = sumOfPowers(nums2, numsSize2, k);
    printf("结果: %d\n", result); // 输出结果: 0

    int nums3[] = {4, 3, -1};
    int numsSize3 = sizeof(nums3) / sizeof(nums3[0]);
    k = 2;
    result = sumOfPowers(nums3, numsSize3, k);
    printf("结果: %d\n", result); // 输出结果: 10

    return 0;
}

 

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值