LeetCode刷题,最长上升序列(动态规划),最少硬币和(动态规划),水壶问题(bfs),生成括号(dfs)

最长上升序列
这题典型的动规题嘛,选选与不选的问题。
如果全是上升的自然全选咯,难搞的地方主要是,有些能选的点,不知道该不该选,比如题目中的示例:
状态转移图
由图可知…emmm画得不太直接,反正就是在如果选择了某个位置时最长的情况是多少,方便回溯时直接读取。
dp[i]的含义就是上文了。
转移方程就是 dp[j] = 前面所有小于j点值的数字选择一个最大的+1;
初始化:由于每个位置至少有该位置那一个数字,所以dp数组全部置1.

class Solution {
   public static int lengthOfLIS(int[] nums) {
        if(nums.length==0){
            return 0;
        }
    
        int dp[] = new int[nums.length];
        Arrays.fill(dp,1);
        int max =1;
        for(int i=1;i<nums.length;i++){
            for(int j=i-1;j>=0;j--){
                if(nums[j]<nums[i]){
                    dp[i] = Math.max(dp[j]+1,dp[i]);
                    
                }
            }
            max = Math.max(dp[i],max);
        }
        return max;
    }
}

还有一种思路,就是dp数组存的是,长度为i时,序列末尾最小的数字是多少(由于,末尾数字最小自然可以后面更有可能接上嘛),然后再二分查找这个数组,找出比本次数字小且最靠右的数字。这种方法能使时间复杂度降为O(n^2);
零钱兑换
这以这样想,如果需要兑换n块钱,那么可以用n枚1元硬币(假设有1元硬币),那么更少的硬币数肯定是,将x元用一个x元额度的硬币替换。能够替换的额度越大,所用硬币数最少。
dp[i]:最少多少枚硬币能兑换i元。
转移方程:dp[i] = min(dp[i],dp[i-j]+1);
初始化:由于可能没有一元硬币(也就是说不一定能恰好换开),所以dp数组全部初始化为n+1元(反正一个尽可能大的数字嘛)

class Solution {
     public static int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount+1];
        Arrays.fill(dp,amount+1);
        dp[0] = 0;
        for(int i=1;i<=amount;i++){
            for(int j=0;j<coins.length;j++){
                if(i>=coins[j]) {
                    dp[i] = Math.min(1 + dp[i - coins[j]], dp[i]);
                }
            }
        }
        return dp[amount]>amount?-1:dp[amount];
    }
}

水壶问题
本来俺以为这是一道数学题(看了答案还真有数学解)。
想着有俩瓶子,就是不断的倒(三种操作),然后不断变化水量,使它总和为目标值,或者所有状态遍历完了都不行。
那么想一哈一共有几种情况。

  • x倒掉
  • y倒掉
  • x倒入y,x能倒完。
  • x倒入y,x不能倒完。
  • y倒入x,y能倒完。
  • y倒入x,y不能倒完。
  • 倒满x
  • 倒满y
    那么就阔以就按这种思路写就好啦。

状态转移图
所有的情况我没有画完,看这花里胡哨的,总之我们得出,几乎状态之间都能相互转换,所以我们需要用一个啥记录哪些状态有没有走过。

class Solution {
    public static boolean canMeasureWater(int x, int y, int z) {
        if (z > x + y) {
            return false;
        }
        if (z == 0 || x == 0 || y == 0) {
            return z == 0 || (x + y == z);
        }
        Queue<int[]> bfs = new LinkedList<>();
        Set<State> set = new HashSet<>();
        bfs.add(new int[]{0, 0});
        while (!bfs.isEmpty()) {
            int[] in = bfs.poll();
            if(in[0]+in[1]==z){
                return true;
            }
            State s = new State(in[0],in[1]);
            if(set.contains(s)){
                continue;
            }
            set.add(s);
            bfs.add(new int[]{in[0],y});
            bfs.add(new int[]{x,in[1]});
            int k =in[0]+in[1]-y;
            bfs.add(new int[]{k<0?0:k,k<0?(in[0]+in[1]):y});
            k = in[1]+in[0]-x;
            bfs.add(new int[]{k<0?(in[0]+in[1]):x,k<0?0:k});
            bfs.add(new int[]{0,in[1]});
            bfs.add(new int[]{in[0],0});
        }

        return false;
    }

}

class State{
    int x,y;
    State(int xx,int yy){
        x=xx;
        y=yy;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj == this){
            return true;
        }
        if(obj==null||getClass()!=obj.getClass()){
            return false;
        }
        State s = (State) obj;
        return s.x==x&&s.y==y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x,y);
    }
}

括号生成
这题贼简单,用dfs遍历所有的情况即可,不过注意不满足条件的不能加入进去,比如互不配对的情况。
配对需要满足的条件是,再加括号时,左括号总比右括号用得多,然后,左括号数只能有n个

class Solution {
      public static List<String> generateParenthesis(int n) {
        List<String> ans = new ArrayList<>();
        dfs(ans,n,n,new StringBuilder());
        return ans;
    }
    private static void dfs(List<String> ans,int right,int left,StringBuilder s){
        if(right==0&&left==0){
            ans.add(String.valueOf(s));
            s.deleteCharAt(s.length()-1);
            return;
        }
        if(right>0) {
            dfs(ans, right - 1, left, s.append("("));
        }
        if(left>right) {
            dfs(ans, right, left - 1, s.append(")"));

        }
        if(s.length()!=0) {
            s.deleteCharAt(s.length() - 1);
        }

    }
}

由于俺,一开始左右写错了,就反着看吧。

发布了18 篇原创文章 · 获赞 1 · 访问量 280

猜你喜欢

转载自blog.csdn.net/qq_38732834/article/details/105185310
今日推荐