Day2-2021.1.10 背包问题的学习+整理。little。

2021年1月10日 时间都去哪了?

今日计划:力扣题目刷到150。javaweb进行下去,或者学习计算机网络。

今日工作:力扣题目刷到147。主要是背包问题还挺难搞???
1.背包问题的学习+整理。
2.416. 分割等和子集
3.解决了Typora笔记上传到CSDN的图片失效问题。
4.逛县城。试做小火锅,经验++。傍晚跑步

今日总结:
在家的效率是打了大大的折扣。
年前补基础,年后找实习。✔

今日语录:
女作家多丽丝·莱辛在《幸存者回忆录》中,写过这样一段话:
“我们浪费自己的健康,去赢得个人的财富,然后又浪费自己的财富,去重建自己的健康。
“我们焦虑地憧憬着未来,忘记了眼前的生活,活得既不是为了现在,也不是为了未来。
“我们活得似乎永远不会死,我们死得好像从来没活过。”

明天计划:整理背包问题的笔记。力扣题目刷到150+。
明天是新的一周了。下周刷题20道。力扣到达170。开始学习项目吧。
可以研读一下:大雪菜的背包九讲+也可以加入大雪菜的寒假刷题班5元

转到2021.1.9-2021.1.31的learning record 首页

[9 5.mp4]

0-1背包问题 视频教程

相关问题
「力扣」上的 0-1 背包问题:

「力扣」第 416 题:分割等和子集(中等);
「力扣」第 474 题:一和零(中等);
「力扣」第 494 题:目标和(中等);
「力扣」第 879 题:盈利计划(困难);
「力扣」上的 完全背包问题:

「力扣」第 322 题:零钱兑换(中等);
「力扣」第 518 题:零钱兑换 II(中等);
「力扣」第 1449 题:数位成本和为目标值的最大数字(困难)。
这里要注意鉴别:「力扣」第 377 题,不是「完全背包」问题。

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-xiang-jie-zhen-dui-ben-ti-de-yo/

贪心算法:优先放入平均价值最高的物品。

本题通过实际的案例,发现在这里贪心算法不可以得到全局最优解。

20190628225225471

通过背包问题,引入动态规划的解决办法。

背包容量c固定,具有重量和价值特征的n种物品,挑出0-n个物品,装进背包,使得背包装入的物品总价值最高。

image-20210109141301474

上图表示了0-1背包问题的状态转移方程。

F(n,c):将n个物品装进容量为c的背包,使得价值最大。

F(i,c)=max( F(i-1,c) , v(i) + F( i-1,c- w(i) ) )

F(i-1,c):第i个物品不放进背包中。

F( i-1,c- w(i) ) :第i个物品放进背包中,相应的,当前的背包容量=背包容量 - 物品i的重量w(i)。

(1)递归方法
public class p9_ex5_1_1 {
    public int knapsack01(int[] w, int[] v, int C) {
        int n = w.length;
        //用[0,1,... ...,index]的物品来填充体积为c的背包的最大价值
        // n-1 是说 物品 0到n-1
        return bestValue(w, v, n - 1, C);
    }

    private int bestValue(int[] w, int[] v, int index, int c) {
        // 边界条件
        // 在调用过程中,index是逐渐减少的,容量c也是逐渐减少的。
        if (index < 0 || c < 0) {
            return 0;
        }
        //第index个物品不放入背包里。
        int res = bestValue(w, v, index - 1, c);
        // 判断剩余容量与w[index] 判断第 index 个物品的体积是否 小于等于背包的剩余容量。
        // 否则直接返回上面得到的res。
        if (w[index] <= c) {
            //第index个物品放入背包里。
            res = Math.max(res, v[index] + bestValue(w, v, index - 1, c - w[index]));
        }
        return res;
    }
}
(2)记忆搜索
import java.util.Arrays;

// 记忆化搜索
public class p9_ex5_1_1_1 {
    int[][] memo;

    public int knapsack01(int[] w, int[] v, int C) {
        int n = w.length;
        // 这里的初始化 容量是 从0到c,是c+1个数。
        memo = new int[n][C + 1];
        for (int i = 0; i < n; i++) {
            Arrays.fill(memo[i], -1);
        }
        //用[0,1,... ...,index]的物品来填充体积为c的背包的最大价值
        // n-1 是说 物品 0到n-1
        return bestValue(w, v, n - 1, C);
    }

    private int bestValue(int[] w, int[] v, int index, int c) {
        // 边界条件
        // 在调用过程中,index是逐渐减少的,容量c也是逐渐减少的。
        if (index < 0 || c < 0) {
            return 0;
        }
        if (memo[index][c] != -1) {
            return memo[index][c];
        }
        //第index个物品不放入背包里。
        int res = bestValue(w, v, index - 1, c);
        // 判断剩余容量与w[index] 判断第 index 个物品的体积是否 小于等于背包的剩余容量。
        // 否则直接返回上面得到的res。
        if (w[index] <= c) {
            //第index个物品放入背包里。
            res = Math.max(res, v[index] + bestValue(w, v, index - 1, c - w[index]));
        }
        memo[index][c] = res;
        return res;
    }
}
(3)动态规划
import java.util.Arrays;

public class p9_ex5_3 {
    public static void main(String[] args) {
        int[] w = new int[]{1, 2, 3};
        int[] v = new int[]{6, 10, 12};
        int c = 5;
        System.out.println(knapsack01(w, v, c));
    }

    private static int knapsack01(int[] w, int[] v, int c) {
        int n = w.length;
        // 没有可供选择的物品
        if (n == 0) {
            return 0;
        }
        // c+1是为了遍历0到c这些重量。 c+1 个元素 遍历列元素。
        int[][] dp = new int[n][c + 1];
        // dp 第0行的元素是多少。
        // 因为下面的下面的双重for循环会使用i-1,所有先去计算得到i=0的数据。
        // dp[0][j] 是说考虑放入第0件物品。如果剩余容量≥w[0],则可以放入。
        for (int j = 0; j <= c; j++) {
            // 如果剩余容量j≥第0个物品的重量,那就可以将第0个物品放入背包,此时背包的价值为v[0]
            // 三目运算符
            dp[0][j] = j >= w[0] ? v[0] : 0;
        }
        for (int i = 1; i < n; i++) {
            for (int j = 0; j <= c; j++) {
                // dp[i][j]是指考虑0到i这些物品,且容积为j的背包,所得到的最大值是多少。
                // 1.不考虑第i件物品。
                dp[i][j] = dp[i - 1][j];
                // 剩余的容量j仍然可以将重量为w[i]的物品装进去。
                if (j >= w[i]) {
                    dp[i][j] = Math.max(dp[i][j], v[i] + dp[i - 1][j - w[i]]);
                }
            }
        }
        // dp初始化大小为[n][c+1]
        return dp[n - 1][c];
    }
}

[9 6.mp4]

(4)背包问题的优化

img

F(i,c)=max( F(i-1,c) , v(i) + F( i-1,c- w(i) ) )

F(i-1,c):第i个物品不放进背包中。

F( i-1,c- w(i) ) :第i个物品放进背包中,相应的,背包容量-物品i的重量w(i)。

第i行元素,只是依赖于第i-1行元素,因此理论上只保留两行元素即可。

代码如下:

public class p9_ex5_3_2_1 {
    public static void main(String[] args) {
        int[] w = new int[]{1, 2, 3};
        int[] v = new int[]{6, 10, 12};
        int c = 5;
        System.out.println(knapsack01(w, v, c));
    }

    private static int knapsack01(int[] w, int[] v, int c) {
        int n = w.length;
        if (n == 0) {
            return 0;
        }
        // 第i行元素,只是依赖于第i-1行元素,因此理论上只保留两行元素即可。
        int[][] dp = new int[2][c + 1];
        for (int j = 0; j <= c; j++) {
            dp[0][j] = j >= w[0] ? v[0] : 0;
        }
        // i j 还是正常的去遍历。但是dp的空间是限制在了两行元素。
        // dp[行的索引i或者i-1 % 2]
        for (int i = 1; i < n; i++) {
            for (int j = 0; j <= c; j++) {
                dp[i % 2][j] = dp[(i - 1) % 2][j];
                if (j >= w[i]) {
                    dp[i % 2][j] = Math.max(dp[i % 2][j], v[i] + dp[(i - 1) % 2][j - w[i]]);
                }
            }
        }
        return dp[(n - 1) % 2][c];
    }
}

[视频9-6 15min30s讲到了 0-1背包问题的变种]

但是并没有详细去引申。自己可以去研读一下:大雪菜的背包九讲+也可以加入大雪菜的寒假刷题班5元。但是实际面试应该够了,不会考的很深。

(1)完全背包问题

完全背包问题:每个物品可以无限次的使用。这里由于背包的容量有限制,所以实际上每个物品的个数也会被限制,所以可以将完全背包问题转化为0-1背包问题。

(2)多重背包问题

多重背包问题:每个物品不止1个,而是有num[i]个。感觉就是上面问题的拓展延伸。

(3)多维费用的背包问题

多维费用的背包问题:考虑体积和重量两个维度

(4)等等变形的问题

[9 7.mp4]

416. 分割等和子集

image-20210110100137128

对上面图片的解释:

题目要求:给定一个只包含正整数非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。那这样两个子集的元素和相等,那么所有元素遍历求和,除以2,得到目标值。目标值需要是整数。sum%2!=0

F(n,c)这里的c=sum/2。F(n,c)是将n个物品填满容量为c的背包。

F(i,c)=F(i-1,c)||F(i-1,c-w(i))。||的解释:因为要求结果是布尔值,那么只要这两个条件中一条满足,即可返回true。

F(i-1,c)是说第i个物品不装进背包。

F(i-1,c-w(i))是说将第i个物品装进背包。

伪代码:

1.遍历数组元素求和,sum%2!=0

2.边界条件:sum=0.sum<0.index<0.

3.业务代码

4.实际上,你考虑:sum/2在一个业务代码中满足了,那么剩余的元素和肯定也是sum/2。

(1)递归方法

递归会超时。

public class p9_ex7_416 {
    //用[0,......,index]的数组填充容量为sum的背包
    public boolean canPartition(int[] nums) {
        int n = nums.length;
        int sum = 0;
        // 首先,对输入的数组进行遍历求和。如果sum是偶数,则可以继续,否则直接返回false。
        for (int i = 0; i < n; i++) {
            sum = sum + nums[i];
        }
        // 如果sum不是偶数。那么就不能被平分。
        if (sum % 2 != 0) {
            return false;
        }
        // n-1 是对0到n-1的物品进行处理。
        // tryPartition解决了 0到n-1的物品 能否填充 sum/2 的背包。
        return tryPartition(nums, n - 1, sum / 2);
    }

    // 这里是使用nums[0...index]的物品,看是否能够完全填充一个容量为sum的背包
    private boolean tryPartition(int[] num, int index, int sum) {
        // 边界条件
        if (sum == 0) {
            return true;
        }
        // 边界条件
        if (sum < 0 || index < 0) {
            return false;
        }
        // 业务代码 F(i,c)=F(i-1,c)||F(i-1,c-w(i))
        //F(i-1,c)是说第i个物品不装进背包。
        //F(i-1,c-w(i))是说将第i个物品装进背包。
        return tryPartition(num, index - 1, sum) || tryPartition(num, index - 1, sum - num[index]);
    }
}
(2)记忆化搜索
import java.util.Arrays;

public class p9_ex7_416 {
    //memo[index][sum]用[0,......,index]的数组填充容量为sum的背包,是否可以填满
    //-1表示未填充,0表示填充不满,1表示可以填充
    int[][] memo;

    //用[0,......,index]的数组填充容量为sum的背包
    public boolean canPartition(int[] nums) {
        int n = nums.length;
        int sum = 0;

        // 首先,对输入的数组进行遍历求和。如果sum是偶数,则可以继续,否则直接返回false。
        for (int i = 0; i < n; i++) {
            sum = sum + nums[i];
        }
        // 如果sum不是偶数。那么就不能被平分。
        if (sum % 2 != 0) {
            return false;
        }
        memo = new int[n][sum / 2 + 1];
        for (int i = 0; i < n; i++) {
            Arrays.fill(memo[i], -1);
        }
        // n-1 是对0到n-1的物品进行处理。
        // tryPartition解决了 0到n-1的物品 能否填充 sum/2 的背包。
        return tryPartition(nums, n - 1, sum / 2);
    }

    // 这里是使用nums[0...index]的物品,看是否能够完全填充一个容量为sum的背包
    private boolean tryPartition(int[] num, int index, int sum) {
        // 边界条件
        if (sum == 0) {
            return true;
        }
        // 边界条件
        if (sum < 0 || index < 0) {
            return false;
        }
        if(memo[index][sum] != -1)
            return memo[index][sum]==1?true:false;
        memo[index][sum]=(tryPartition(nums,index-1,sum)||tryPartition(nums,index-1,sum-nums[index]))==true?1:0;
        return tryPartition(nums,index-1,sum)||tryPartition(nums,index-1,sum-nums[index]);  
    }
}

性能还是差一点。

image-20210110202958594
(3)动态规划

这个是看的力扣的题解:+感觉比视频要好理解的多。

其实我还是感觉记忆化搜索比动态规划容易理解。

public class p9_ex7_416_1_1_1 {
    public static void main(String[] args) {
        int[] nums = new int[]{1, 5, 11, 5};
        System.out.println(canPartition(nums));
    }

    public static boolean canPartition(int[] nums) {
        int n = nums.length;
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += nums[i];
        }
        if (sum % 2 != 0) {
            return false;
        }
        int c = sum / 2;
        // 这里的数组初始化后,默认值是false。
        boolean[][] dp = new boolean[n][c + 1];
        // 因为下面的句子会用到dp[i - 1][j]。所以先填表格第 0 行,第 1 个数只能让容积为它自己的背包恰好装满
        if (nums[0] <= c) {
            dp[0][nums[0]] = true;
        }
        for (int i = 1; i < n; i++) {
            for (int j = 0; j <= c; j++) {
                //不将第i件物品装进背包。
                dp[i][j] = dp[i - 1][j];
                // 下面的if可以删掉,不影响结果。
                if (nums[i] == j) {
                    dp[i][j] = true;
                    continue;
                }
                // 剩余容量可以装入第i件物品
                if (nums[i] < j) {
                    dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
                }
            }
        }
        return dp[n - 1][c];
    }
}

[3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据星历计算卫星位置的方法是比较复杂的,需要用到天文学中的一些计算方法和公式,因此需要一些较为复杂的计算。以下是一个大致的计算流程: 1. 将给定的日期时间转换为儒略日(Julian day)。 2. 根据儒略日计算相应的世纪数。 3. 计算卫星的平近点角(Mean Anomaly)和升交点赤经(Right Ascension of Ascending Node)。 4. 计算卫星的轨道倾角(Inclination)、近地点角距(Argument of Perigee)、半长轴(Semi-Major Axis)等轨道参数。 5. 计算卫星的真近点角(True Anomaly)。 6. 根据卫星的真近点角、轨道参数和世纪数等信息,计算卫星的位置。 下面是一个简单的 C++ 代码示例,可以用来计算给定日期时间的 C01 卫星位置: ```c++ #include <iostream> #include <cmath> using namespace std; const double PI = 3.14159265358979323846; const double GM = 3.986005e14; const double OMEGA_E = 7.2921151467e-5; const double MU_GPS = 3.986005e14; const double C_GPS = 299792458.0; // 计算儒略日 double julian_day(int year, int month, int day, int hour, int minute, int second) { int a = (14 - month) / 12; int y = year + 4800 - a; int m = month + 12 * a - 3; int jdn = day + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045; double jd = jdn + ((double)hour - 12) / 24 + (double)minute / 1440 + (double)second / 86400; return jd; } // 计算卫星位置 void satellite_position(double t, double *pos) { // 从星历中读取轨道参数等信息 double a = 26559700.0; double e = 0.008777161321; double i = 0.9757632172; double O = 0.5405171530; double w = 3.510029399; double M = 0.0; double n = sqrt(MU_GPS / pow(a, 3)); double t0 = 0.0; // 计算平近点角 double E = M + e * sin(M); double E0 = E; while (abs(E - E0) > 1e-12) { E0 = E; E = M + e * sin(E); } double v = 2 * atan(sqrt((1 + e) / (1 - e)) * tan(E / 2)); double r = a * (1 - e * cos(E)); // 计算卫星位置 double x = r * (cos(O) * cos(w + v) - sin(O) * sin(w + v) * cos(i)); double y = r * (sin(O) * cos(w + v) + cos(O) * sin(w + v) * cos(i)); double z = r * sin(w + v) * sin(i); double dt = t - t0; pos[0] = x * cos(OMEGA_E * dt) + y * sin(OMEGA_E * dt); pos[1] = -x * sin(OMEGA_E * dt) + y * cos(OMEGA_E * dt); pos[2] = z; } int main() { int year = 2020; int month = 12; int day = 23; int hour = 23; int minute = 0; int second = 0; double jd = julian_day(year, month, day, hour, minute, second); double t = (jd - 2451545.0) / 36525.0; double pos[3]; satellite_position(t, pos); cout << "C01卫星位置:" << endl; cout << "X = " << pos[0] << " m" << endl; cout << "Y = " << pos[1] << " m" << endl; cout << "Z = " << pos[2] << " m" << endl; return 0; } ``` 注意:由于卫星位置的计算涉及很多天文学中的参数和公式,因此以上代码仅供参考,具体实现需要根据实际情况进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值