LeetCode1049. 最后一块石头的重量 II / 牛客:毕业旅行问题(状压DP)
Posted Zephyr丶J
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode1049. 最后一块石头的重量 II / 牛客:毕业旅行问题(状压DP)相关的知识,希望对你有一定的参考价值。
1049. 最后一块石头的重量 II
2021.6.8每日一题
有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。
每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。
示例 1:
输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。
示例 2:
输入:stones = [31,26,33,21,40]
输出:5
示例 3:
输入:stones = [1,2]
输出:1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/last-stone-weight-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
五个月前会做的题,现在想不到咋做!
还是想办法转换成0-1背包问题
class Solution {
public int lastStoneWeightII(int[] stones) {
//这怎么动规
//不会就先想暴力解法,暴力解就是回溯,复杂度n!
//回溯过程中,变化的是下标和最终结果,数组好像也变,这咋办
//看了官方题解,说实话,证明看的一知半解
//但是隐约get到一点,就是相撞的过程并不需要考虑,即具体哪个石头撞并不需要去考虑,而只需要使两堆石头接近就可以
//而这样两堆石头相撞能否得到绝对值之差呢,显然是可以的
//因此,这样就可以用动态规划,来找到这个一堆中的最大值,也就是最接近sum / 2的值
int sum = 0;
int l = stones.length;
for(int stone : stones)
sum += stone;
int target = sum / 2;
//i个石头是否能凑出j的重量
boolean[][] dp = new boolean[l + 1][target + 1];
//初始化
dp[0][0] = true;
for(int i = 1; i <= l; i++){
for(int j = 0; j <= target; j++){
dp[i][j] = dp[i - 1][j];
if(j >= stones[i - 1]){
dp[i][j] |= dp[i - 1][j - stones[i - 1]];
}
}
}
int res = 0;
for(int j = target; j >= 0; j--){
if(dp[l][j]){
res = j;
break;
}
}
return sum - res * 2;
}
}
状态压缩
class Solution {
public int lastStoneWeightII(int[] stones) {
int sum = 0;
int l = stones.length;
for(int stone : stones)
sum += stone;
int target = sum / 2;
//i个石头是否能凑出j的重量
boolean[] dp = new boolean[target + 1];
//初始化
dp[0] = true;
for(int i = 1; i <= l; i++){
for(int j = target; j >= 0; j--){
dp[j] = dp[j];
if(j >= stones[i - 1]){
dp[j] |= dp[j - stones[i - 1]];
}
}
}
int res = 0;
for(int j = target; j >= 0; j--){
if(dp[j]){
res = j;
break;
}
}
return sum - res * 2;
}
}
毕业旅行问题
题目描述
链接:https://www.nowcoder.com/questionTerminal/3d1adf0f16474c90b27a9954b71d125d
来源:牛客网
小明目前在做一份毕业旅行的规划。打算从北京出发,分别去若干个城市,然后再回到北京,每个城市之间均乘坐高铁,且每个城市只去一次。由于经费有限,希望能够通过合理的路线安排尽可能的省一些路上的花销。给定一组城市和每对城市之间的火车票的价钱,找到每个城市只访问一次并返回起点的最小车费花销。
输入描述:
城市个数n(1<n≤20,包括北京)
城市间的车票价钱 n行n列的矩阵 m[n][n]
输出描述:
最小车费花销 s
示例1
输入
4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0
输出
13
说明
共 4 个城市,城市 1 和城市 1 的车费为0,城市 1 和城市 2 之间的车费为 2,城市 1 和城市 3 之间的车费为 6,
城市 1 和城市 4 之间的车费为 5,依次类推。
假设任意两个城市之间均有单程票可购买,且票价在1000元以内,无需考虑极端情况。
思路
最小路径问题,好久没做过了,来复习一下,最简单的办法就是深度优先搜索吧
字节这题也太难了点吧…
状态压缩dp,有点难理解,学习一下
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[][] dist = new int[n][n];
for (int i = 0; i < dist.length; i++) {
for (int j = 0; j < n; j++) {
dist[i][j] = in.nextInt();
}
}
in.close();
//随便挑一个作为起点,为方便计算,就选择0城市就好,
//那么对应的事件可以理解为从0号城市出发去(1号、2号、3号城市)最后回到0号城市,其中中间的1、2、3之间的顺序不清楚怎么去的。
//但是可以发现事件最小消耗为min(从0->1的距离+从1号到{2号、3号}最后回到0的距离、
// 从0->2的距离+从2号到{1号、3号}最后回到0的距离、
// 从0->3的距离+从1号到{1号、2号}最后回到0的距离)。
// 用bit第几位是否为1表示当前事件有哪几号城市。{2号、3号}——> 110;
//具体可以再细分,这样就可以用状态压缩的动态规划来求解
//事件的最大值,也就是最大是n-1位都为1
int V = 1 << (n - 1);
//表示从i城市出发,经过j中各个城市回到i城市的最短路径
int[][] dp = new int[n][V];
//初始化,从0号城市到达0号城市的距离是0
dp[0][0] = 0;
for (int j = 0; j < V; j++) {
for (int i = 0; i < n; i++) {
if (j == 0) {
//表示从第i个城市到第j(0)个城市的最小路径。正好就是第i个城市去第0个城市的距离。
dp[i][j] = dist[i][j] + dp[0][0];
} else {
//当j不等于0时,枚举所有城市,如果此时经过了其中一个城市,那么dp[i][j]就可以由这个城市转移而来,即先到这个城市,再由这个城市到其他城市
dp[i][j] = Integer.MAX_VALUE;
if(((j >> (i - 1)) & 1) == 0) {
/*此式子代表当dp[i][j]当j中的二进制数从右面数第i个数为零时,才进行后面运算,因为i代表以该城市为起点,后面不能再经过该城市*/
for (int k = 1; k < n; k++) {
//表示第k位城市。
int index = (1 << (k - 1));
//当前的dp应该是遍历了j城市集的每一个城市对应的子dp + i到k的距离,求得的其中的最小的那个值;
if ((index & j) > 0) {
//找到其中的一个k;
//表示j城市集内除了第k位其他的别的城市
int other = j ^ index;
//从城市i到城市k,再从城市k到其他城市
dp[i][j] = Math.min(dist[i][k] + dp[k][other], dp[i][j]);
}
}
}
}
}
}
//最后输出,从城市0到其他城市
System.out.println(dp[0][V - 1]);
}
}
以上是关于LeetCode1049. 最后一块石头的重量 II / 牛客:毕业旅行问题(状压DP)的主要内容,如果未能解决你的问题,请参考以下文章