练习题+题解:链表+dp

1.链表指定区间反转

题目描述

将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度O(n),空间复杂度O(1)。

输入格式:

输入三行(多组实例测试)
第一行size(表示链表长度,0<size<=1000
第二行输入size个数,为每个节点对应值val(|val|<=1000
第三行为m,n(为反转区间的起止位置,0<m<=n<=size

输出格式:

输出若干行,每行为每次区间反转后的结果

输入样例:

5
1 2 3 4 5
1 3
5
1 2 3 4 5
2 4

输出样例:

3 2 1 4 5
1 4 3 2 5

题目分析

本题要求多组数据输入且每组数据让我们先输入一个size,我们考虑使用scanf("%d", &size) != EOF这样的一个结构来控制循环。
题目思路:首先我们可以先想想整条链表如果要反转应该怎么做,我们可以先创建3个节点指针,n1指向空,n2指向头结点,n3指向n2的下一个节点,然后我们让n2指向n1,让n1到n2位置,n2到n3位置,n3指向n2的下一个节点。以此循环当n2为空时或者循环节点数次时我们结束循环。图片解析如下所示:
在这里插入图片描述

此方法并不唯一,也有其他方法。接下来我们用这个方法来解答指定区间反转问题。
我们可以将n2指向反转区间的头结点,n1继续为空,最后循环次数为(n-m)+1次,n为区间头,m为区间尾。循环结束之后,n1指向区间尾节点,n2指向尾部断开的下一个节点,在通过链接就完成了区间倒转。 图片解析如下所示:
在这里插入图片描述

代码实现

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef struct ListNode {
	int val;
	struct ListNode* next;
}L;//节点
void CreateL(L** phead, int x)//创建链表
{
	L* newNode = (L*)malloc(sizeof(L));
	if (newNode == NULL) {
		perror("malloc fail!");
		return;
	}
	newNode->val = x;
	newNode->next = NULL;
	if (*phead == NULL) {
		*phead = newNode;
		return;
	}
	L* pcur = *phead;
	while (pcur->next) {
		pcur = pcur->next;
	}
	pcur->next = newNode;
}
void PrintL(L* phead)//打印链表
{
	assert(phead);
	L* pcur = phead;
	while (pcur) {
		printf("%d ", pcur->val);
		pcur = pcur->next;
	}
	printf("\n");
}
void InvertL(L** phead,int m,int n)//反转链表
{
	L* n1 = NULL, * n2 = *phead, * n3 = NULL;
	L* pcur = *phead;
	L* phead1 = *phead;//区间开始节点
	for (int i = 1; i < m-1; i++)
	{
		pcur=pcur->next;//区间节点前一个节点
	}
	if(m!=1)phead1 = pcur->next;
	n2 = phead1;//插入区间节点的头结点
	n3 = n2->next;
	for (int i = 0; i <= (n - m); i++) {//指定区间反转循环
		n2->next = n1;
		n1 = n2;
		n2 = n3;
		if(n2!=NULL)n3 = n2->next;//n2不是空,n3才能指向n2的next
	}
	if (m==1) {
		*phead = n1;
		phead1->next = n2;
	}
	else
	{
		pcur->next = n1;
		phead1->next = n2;
	}
}
int main()
{
	int size; 
	while (scanf("%d", &size) != EOF)
	{
		L* plist = NULL;
		for (int i = 0; i < size; i++) {
			int x; scanf("%d", &x);
			CreateL(&plist, x);
		}
		int m, n;
		scanf("%d %d", &m, &n);
		InvertL(&plist, m, n);
		PrintL(plist);
	}
	return 0;
}

2.hxj和他的甜品盲盒I

hxj很爱吃甜品。有一天,他买了一个甜品盲盒,盲盒有x个小盒子(依次编号为1、2、3……x),每个小盒子排成一排,每个小盒子里都有一个甜品,每个甜品都有一个对应的幸福值。
由于需要控制每天糖分的摄入量,他给自己设下规矩:不能选择相邻的甜品。请你帮他计算一下他能获得的最大幸福值。

输入格式:

输入两行
第一行为x(小盒子数量,1<=x<=100
第二行为x个值,每个值分别代表每个小盒子中甜品可获得的幸福值。(0<=幸福值<=400

输入样例:

4
1 2 3 1

输出样例:

4

样例解释

hxj选择吃1号甜品 (幸福值 = 1) ,然后选择吃 3 号甜品 (幸福值 = 3)。获得到的最高幸福值 = 1 + 3 = 4 。

输入样例:

5
2 7 9 3 1

输出样例:

12

样例解释

hxj选择吃 1 号甜品(幸福值 = 2),选择3 号甜品 (幸福值 = 9),接着吃 5 号甜品 (幸福值 = 1)。 最后获得到的最大幸福值= 2 + 9 + 1 = 12 。

题目分析

本题是一道动态规划题
我们可以创建两个dp表,f和g,每个dp表中有x(盲盒数量)个元素。
接下来,我们开始考虑往f表和g表中填入数据
f[i]用来表示吃nums[i]甜品获得最大的幸福值,g用来表示不吃nums[i]甜品获得的幸福值。接下来我们就可以得到状态方程:
(1)f[i]=nums[i]+g[i-1](吃nums[i]甜品就不能吃nums[i-1]甜品)
(2)g[i]=max(f[i-1],g[i-1])(不吃nums[i]甜品我们可能吃nums[i-1]甜品,不一定要吃,所以我们选择吃或者不吃nums[i-1]甜品的最大值)

根据上面的状态方程我们就可以填写f和g两个dp表了,最后取吃nums[n-1]和不吃nums[n-1]甜品的最大值,即取f[n-1]和g[n-1]的最大值。(取n-1原因:数组下标从0开始)。

Java代码实现

import java.util.Scanner;
class Solution
{
    public int happy(int[] nums) {
        if(0 > nums.length-1) return 0;
        int n = nums.length;
        int[] f= new int[n];//创建f和g表
        int[] g= new int[n];
        f[0]=nums[1];
        g[0]=0;//两个表初始值写入
        for(int i = 1; i <= n-1; i++)//按顺序填表
        {
            f[i] = g[i - 1] + nums[i];
            g[i] = Math.max(g[i - 1], f[i - 1]);
        }
        return Math.max(f[n-1], g[n-1]);//返回最大幸福值
    }
}
public class Main {
    public static void main(String[] args) {
        Scanner scan=new Scanner(System.in);
        int x= scan.nextInt();
        int[] nums=new int[x];
        for (int i = 0; i < x; i++) {
            nums[i]= scan.nextInt();
        }
        Solution solution=new Solution();
        int ret=solution.happy(nums);
        System.out.println(ret);
    }
}

3.First 50 Prime Numbers

Your program reads one natural numbers n in, and prints out the sum of the first n prime numbers starting from 2.

输入格式

A positive whole numbers n, which is less than 1000

输出格式

A number which is the sum of all the first n prime numbers.

输入样例

10

输出样例

129

题目解析

本题意思为:输入一个自然数n,将前n个素数相加,输出相加的结果。

代码实现

#include<stdio.h>
int main()
{
    int add=0;
    int n;
    scanf("%d",&n);
    for(int i=2;n>0;i++)
    {
        if(i==2)//2为特殊值,单拉出来考虑,不在循环前考虑防止n为0的情况
        {
            add+=2;
            n--;
            continue;
        }
        int flag=1;
        //下面循环语句判断素数
        for(int j=2;j<i;j++)
        {
            if(i%j==0)
            {
                flag=0;
                break;
            }
        }
        //如果为素数就加起来,并让n减1
        if(flag&&n>0)
        {
            add+=i;
            n--;
        }
    }
    printf("%d\n",add);
    return 0;
}
  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值