Light OJ 1283 Shelving Books (区间DP)

本文介绍了一个图书上架问题,目标是在限制条件下尽可能多地将不同高度的图书按非递减顺序排列到书架上。通过动态规划的方法求解最大可上架图书数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1283 - Shelving Books
Time Limit: 2 second(s)Memory Limit: 32 MB

You are a librarian. You keep the books in a well organized form such that it becomes simpler for you to find a book and even they look better in the shelves.

One day you get n new books from one of the library sponsors. And unfortunately they are coming to visit the library, and of course they want to see their books in the shelves. So, you don't have enough time to shelve them all in the shelf in an organized manner since the heights of the books may not be same. But it's the question of your reputation, that's why you have planned to shelve them using the following idea:

1)      You will take one book from the n books from left.

2)      You have exactly one shelf to organize these books, so you may either put this book in the left side of the shelf, right side of the shelf or you may not put it in the shelf. There can already be books in the left or right side. In such case, you put the book with that book, but you don't move the book you put previously.

3)      Your target is to put the books in the shelf such that from left to right they are sorted in non-descending order.

4)      Your target is to put as many books in the shelf as you can.

You can assume that the shelf is wide enough to contain all the books. For example, you have 5 books and the heights of the books are 3 9 1 5 8 (from left). In the shelf you can put at most 4 books. You can shelve 3 5 8 9, because at first you got the book with height 3, you stored it in the left side of the shelf, then you got 9 and you put it in the right side of the shelf, then you got 1 and you ignored it, you got 5 you put it in the left with 3. Then you got 5 and you put it in left or right. You can also shelve 1 5 8 9 maintaining the restrictions.

Now given the heights of the books, your task is to find the maximum number of books you can shelve maintaining the above restrictions.

Input

Input starts with an integer T (≤ 200), denoting the number of test cases.

Each case starts with a line containing an integer n (1 ≤ n ≤ 100). Next line contains n space separated integers from [1, 105]. The ith integer denotes the height of the ith book from left.

Output

For each case, print the case number and the maximum number of books that can be shelved.

Sample Input

Output for Sample Input

2

5

3 9 1 5 8

8

121 710 312 611 599 400 689 611

Case 1: 4

Case 2: 6

 


PROBLEM SETTER: JANE ALAM JAN




#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn= 150;
const int INF = 0x3f3f3f3f;
int t,n;
int dp[maxn][maxn][maxn],a[maxn],s[maxn];
int dfs(int p,int l,int r){
    if(dp[p][l][r]!=-1)return dp[p][l][r];
    if(p>n)return 0;
    int res = dfs(p+1,l,r);
    if(a[p]>=a[l]  && a[p]<=a[r]){
        res = max(res,max(dfs(p+1,p,r)+1,dfs(p+1,l,p)+1));
    }
    dp[p][l][r]=res;
    return res;
}
int main()
{
    //freopen("123.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    cin>>t;
    int ca = 0;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        a[0]=0;a[n+1]=INF;
        memset(dp,-1,sizeof dp);
        ca++;
        cout<<"Case "<<ca<<": "<<dfs(1,0,n+1)<<endl;

    }
}


<think>嗯,用户想了解东华大学OJ平台上关于“k倍区间”题目的解题思路。首先,我需要回忆一下“k倍区间”这类题目的常见解法。通常这类问题涉及到求数组中连续子数组的和是k的倍数的情况数。 首先,用户提到的东华大学OJ的具体题目可能是什么样子的呢?假设题目是给定一个数组,要求找出所有子数组中其和是k的倍数的个数。例如,题目可能类似于蓝桥杯的“k倍区间”问题,这类题目的解法通常需要用到前缀和和哈希表来优化时间复杂度。 接下来,我需要考虑解题的关键步骤。首先,前缀和数组的构建是基础,计算每个位置的前缀和。然后,对于每个前缀和,我们需要找到之前出现过的相同余数的前缀和,这样两者之间的子数组的和就是k的倍数。这里需要注意的是余数的处理,尤其是当余数为负数时,可能需要转换为正余数。 用户提供的引用中有提到一些相关的算法思路,比如引用[1]中提到的判断有序区间的方法,可能和数组分段有关,但可能更适用于旋转排序数组的问题。而“k倍区间”更关注前缀和的余数分布。引用[3]中的剪枝方法或许在优化时有用,比如处理重复元素的情况,但可能更适用于DFS剪枝的场景。 然后,我需要确保解题思路的正确性。例如,当计算余数时,是否需要处理负数的情况?例如,如果k是正数,而前缀和为负数,那么余数应该调整为k的补数。例如,余数=(sum % k + k) % k,这样可以确保余数在0到k-1之间。 另外,需要注意前缀和数组的初始化,通常会在哈希表中预先存入余数为0的情况,表示前缀和为0的情况出现一次,这样可以处理从数组开头到当前位置的和是k倍数的情况。 接下来,可能需要给出具体的步骤,例如: 1. 计算前缀和数组。 2. 遍历前缀和数组,计算每个位置的余数。 3. 使用哈希表记录每个余数出现的次数。 4. 统计当前余数在哈希表中已存在的次数,累加到结果中。 5. 更新哈希表中的余数计数。 此外,需要处理k为0的特殊情况吗?通常题目中的k不会是0,但需要根据题目具体描述来决定。假设题目中k是正整数,所以不需要考虑除以0的情况。 然后,可以举一个例子,比如数组是[1,2,3,4,5],k=5,对应的前缀和为[1,3,6,10,15],余数分别为1,3,1,0,0。那么余数为1的出现次数,当第三次遇到1时,前面已经有两次,所以结果会增加两次。余数为0的情况在第一次遇到时,结果加1(因为哈希表初始有0出现一次),以此类推。 用户可能需要具体的代码实现,例如Python的写法。同时,要考虑到时间复杂度和空间复杂度,这样的方法时间复杂度是O(n),空间复杂度是O(k)。 在参考用户提供的引用时,引用[4]提到了搜索旋转排序数组的问题,这可能与数组的分段有序有关,但可能和当前问题关联不大。不过,如果东华大学的题目有特殊变形,可能需要结合其他技巧,但通常“k倍区间”问题还是以余数统计为主。 最后,确保回答的结构清晰,分步骤讲解,并且给出代码示例。同时,生成相关问题,如优化方法、扩展问题等,帮助用户进一步理解。</think>### 东华大学 OJ "k倍区间" 题目解析 #### 题目理解 题目要求统计数组中连续子数组的和是k的倍数的区间个数。例如数组$[4,5,0,-2,-3,1]$,$k=5$时,满足条件的区间有7个,包括$[0], [5,0], [5,0,-2,-3]$等。 #### 解题思路 1. **前缀和思想** 设数组为$arr$,定义前缀和数组$preSum$,其中$preSum[i] = \sum_{j=0}^{i-1} arr[j]$。区间$[i,j]$的和可表示为$preSum[j+1] - preSum[i]$。 2. **余数映射** 若$(preSum[j] - preSum[i]) \mod k = 0$,则$preSum[j] \mod k = preSum[i] \mod k$。因此,只需统计相同余数的前缀和出现次数。 3. **哈希表优化** 使用哈希表记录余数出现的次数。当遍历到$preSum[j]$时,若其余数$r$已出现$m$次,则新增$m$个合法区间。 #### 算法步骤 1. 初始化哈希表`remainder_map`,存入`{0:1}`(处理前缀和本身为k倍数的情况) 2. 计算前缀和$sum$,并计算余数$r = sum \mod k$ 3. 将余数调整为非负数:若$r <0$,则$r +=k$ 4. 累加当前余数在哈希表中已存在的次数到结果中 5. 更新哈希表中该余数的计数 #### 代码实现 ```python def count_k_subarrays(arr, k): remainder_map = {0: 1} current_sum = 0 result = 0 for num in arr: current_sum += num r = current_sum % k # 处理负余数 if r < 0: r += k # 累加已有次数 result += remainder_map.get(r, 0) # 更新哈希表 remainder_map[r] = remainder_map.get(r, 0) + 1 return result ``` #### 复杂度分析 - 时间复杂度:$O(n)$,仅需一次遍历 - 空间复杂度:$O(k)$,哈希表最多存储k种余数 #### 相关扩展 1. 若题目要求输出具体区间而非仅计数,可结合哈希表记录余数对应的索引列表,通过索引差生成区间[^1] 2. 当$k=0$时需特殊处理(题目通常规定$k \geq 1$) 3. 类似思想可解决"和为k的子数组"问题,将余数映射改为差值映射[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值