【基础算法训练】——线性动态规划、链表

目录

    • 前言
    • 第一题 P1095 [NOIP2007 普及组] 守望者的逃离
      • 题目描述
      • 解题报告
      • 参考代码(C++版本)
    • 第二题 1290. 二进制链表转整数
      • 题目描述
      • 解题报告
      • 参考代码(C++版本)
    • 第三题 237. 删除链表中的节点
      • 题目描述
      • 解题报告
      • 参考代码(C++版本)
    • 第四题 剑指 Offer II 024. 反转链表
      • 题目描述
      • 解题报告
      • 参考代码(C++版本)
    • 第五题 1019. 链表中的下一个更大节点
      • 题目描述
      • 解题报告
      • 参考代码(C++版本)
    • 总结

前言

在这里插入图片描述
每日算法练习,千锤百炼,静待花开。

Leetcode的专栏会持续更,因为在跟着英雄哥做知识星球的事儿:在lc被欺负的这些年
对英雄哥的知识星球有兴趣的可以看看他这篇文章喔: 英雄算法联盟 | 31天让你的算法与众不同
但是英雄哥这儿了,五月不再招人啦,有想法的小伙伴浅等六月嗷~
单片机是会持续更的,但是硬件看到人比较少吧,或者我理解它不投策,这个更得慢:十四天学会51单片机

至于现在这个专栏只会记录全部是每日的算法题:知识星球每天的习题,以及在咱高校算法学习社区中,泡泡和p佬普及组和提高组的题目,一般是当天写当天更吧。现在优先写泡泡的题,p佬的有点小把握不住

在这里插入图片描述
好朋友执梗正在带新星计划了,有想法的小伙伴不要犹豫嗷
点击查看详情
在这里插入图片描述

在这里插入图片描述

第一题 P1095 [NOIP2007 普及组] 守望者的逃离

线性动态规划

题目描述

【基础算法训练】——线性动态规划、链表_第1张图片
原题传送门

解题报告

注意题目中的信息,让咱们找到最短时间或者能够跑的最大距离。说人话就是 让咱们找最优解。再看看这个数据范围,暴力是没法暴力了,老老实实动态规划吧。
这个题可以不用闫式DP分析了吧。因为状态转移比较明显了,但还是浅用一下吧。
闫式DP是从集合的角度来分析动态规划的,主要有状态表示状态计算两个环节。

状态表示
状态表示主要是对用于描述问题的这个集合f进行定义,以及对这个集合f最终存放的数据,也就是属性进行确定。
——集合:f[i]表示在i这个时间内,可以跑的距离
——属性:最大值

状态计算
状态计算对应的是集合划分的过程,通常的玩法是选取最后一个不同点,其实也就是看上个状态转移到当前状态有没有不同的地方

对于本题而言,本题是线性DP,当前状态和上个状态分别使用f[i]f[i-1]表示。
f[i-1]f[i]有三种情况
——① 剩余魔法值大于等于10,可使用技能,f[i] = f[i-1] + 60
——② 剩余魔法值小于10,不可使用技能,原地休息,此时没有位移,f[i] = f[i-1]
——③老老实实跑步,f[i] = f[i-1] + 17

将上述文字提炼一下,得到这张闫式DP分析图:
【基础算法训练】——线性动态规划、链表_第2张图片

参考代码(C++版本)

#include  

using namespace std;
typedef long long LL;
const int N = 3e5 + 10;
int f[N];//f[i]表示用i的时间,最多可以跑多远。 
int m,s,t;//初始魔法值、到逃生口距离,所剩时间 

int main()
{
	scanf("%d %d %d",&m,&s,&t);
	
	//优先考虑全部放技能闪烁
	for(int i =1;i <= t;i++)
	{
		//放一次技能十点魔法值
		if(m >= 10)
		{
			m -= 10;
			f[i] = f[i-1] + 60;	
		}else//魔法值不够,原地休息 
		{
			m += 4;
			f[i] = f[i-1]; 
		}	
	} 
	
	//将只是跑步的玩法揉进去,找个每个递推的最大值
	for(int i = 1;i <= t;i++) f[i] = max(f[i],f[i-1]+17);
	
	bool isSuccess = false;
	for(int i = 0; i <= t;i++)
	{
		if(f[i] >= s)
		{
			isSuccess = true;
			puts("Yes");
			printf("%d\n",i);
			break;
		}
	 } 
	
	//用完时间了,都没有出去,输出NO
	if(!isSuccess)
	{
		puts("No");
		printf("%d\n",f[t]); 
	}
	
	return 0;
}

在这里插入图片描述

第二题 1290. 二进制链表转整数

题目描述

【基础算法训练】——线性动态规划、链表_第3张图片
原题传送门

解题报告

采用的常规模拟的思路,先求出当前链表的长度,再逐一的去取链表中每个结点的数据,把取出来的这位数据,转换成十进制数,累加起来,就是需要的结果。
思路挺简单的,效率差了一点。

参考代码(C++版本)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    int getDecimalValue(ListNode* head) {
        //先扫一遍。统计一下链表的长度吧
        ListNode* temp = head;
        int n = 0;
        while(temp)
        {
            temp = temp->next;
            n ++;
        }
        
        //将链表中没一个结点的信息取出来,转成十进制
        int ans = 0;
        temp = head;
        for(int i = n-1;i >= 0; i--)
        {
            if(temp != nullptr)
            {
                ans += (temp->val) * pow(2,i);
                temp = temp->next;
            }
        }
        return ans;
    }
};

在这里插入图片描述

第三题 237. 删除链表中的节点

覆盖

题目描述

【基础算法训练】——线性动态规划、链表_第4张图片
原题传送门

解题报告

不是常规思路,这种使用覆盖来实现删除的想法可以浅积累一下。
补充一下常规思路:
常见的玩法是定位到待删除结点的上一个结点,比如它叫做prenode,通过修改这个prenodenext指针,指向待删除结点的下一个结点,比如叫做nenode,通过这种方式实现删除。
本题了,传入的参数是要被删除的结点,无法定位到上一个结点。因此不能使用常规的方法来实现了。

现在我们有的是node结点,我们是可以通过next指针来使用node结点之后的数据的,也可以删除node结点之后的数据。
我们可以删除node结点的下一个结点,为了出现node被删除的效果,可以让node存储的数据变成其下一个结点存储的数据。

【基础算法训练】——线性动态规划、链表_第5张图片

参考代码(C++版本)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void deleteNode(ListNode* node) {
        //不是常规玩法了
        node->val = node->next->val;
        node->next = node->next->next;
    }
};

在这里插入图片描述

第四题 剑指 Offer II 024. 反转链表

题目描述

【基础算法训练】——线性动态规划、链表_第6张图片
原题传送门

解题报告

按照下面的小字吧,提供两种解法思路,一种是常规的迭代法,一种是递归法,其中迭代的效率要高一点。

迭代法可以画图解,但是今天暂时没有时间,后续补上。

参考代码(C++版本)

解法一:迭代法

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        //特判
        if(head == nullptr) return head;
        //获取头指针的下一个结点
        ListNode* move = head->next;
        //断开头指针和该结点的连接
        head->next = nullptr;

        while(move)
        {
            //存储当前move结点的下一个结点的信息
            ListNode* tmp = move->next;
            //让move翻转指向其前一个结点
            move->next = head;
            //让头指针移动到move指针的位置
            head = move;
            //让move移动到其后面一个结点的位置
            move = tmp;
        }
        return head;
    }
};

解法二:递归法

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head) return head;

        //设置递归结束的条件
        //也就是找到尾结点了
        if(head->next == nullptr) return head;

        ListNode* ans = reverseList(head->next);
        head->next->next = head;//让身为尾结点的head->next指向其前一个结点head,构建出原本尾结点翻转指向前一个结点的新链表
        //让新链表的尾结点指向null
        head->next = nullptr;

		//返回形成新链表
        return ans;
    }
};

在这里插入图片描述

第五题 1019. 链表中的下一个更大节点

题目描述

【基础算法训练】——线性动态规划、链表_第7张图片
原题传送门

解题报告

因为直接操作链表也不太方便,就考虑将数据存储到数组中,直接操作数值吧。
这个题是使用的栈的想法,让当前栈从栈顶到栈底是严格单调递减的,也就是假如新的元素一旦大于栈顶,就把当前的栈顶元素弹出。

参考代码(C++版本)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */

class Solution {
public:
    vector<int> nextLargerNodes(ListNode* head) {
        vector<int> ans;
        ListNode* curr = head;
        //把链表中的时间
        while (curr != nullptr)
        {
            ans.push_back(curr->val);
            curr = curr->next;
        }

        // 返回结果的数组,默认是0
        int n = ans.size();
        vector<int> res(n, 0);
        // 单调栈中保存的是数组的索引,数值是递减, 
        stack<int> stk;

        for (int i = 0; i < n; i++)
        {
            if (stk.empty()) stk.push(i);
            else
            {
                while (!stk.empty() && ans[stk.top()] < ans[i])
                {
                    // 更新更大的数值
                    res[stk.top()] = ans[i];
                    stk.pop();
                }
                stk.push(i);
            }
        }
        return res;
    }
};


在这里插入图片描述

总结

①积累动态规划,因为动归了,有点吃自己的经验
② 今天链表的题大多数是可以根据题目意思进行模拟的,需要注意空链表的情况

你可能感兴趣的:(每日习题浅记录,在lc被欺负的这些年,动态规划,算法,leetcode,链表)