洛谷 P1415 拆分数列【巧妙dp】

本文详细解析洛谷P1415拆分数列问题,通过动态规划的方法找到最优解,实现将一串数字拆分为严格递增数列的目标,并确保最后一个数最小,同时保证字典序最大。

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

洛谷 P1415 拆分数列
题目描述

给出一列数字,需要你添加任意多个逗号将其拆成若干个严格递增的数。如果有多组解,则输出使得最后一个数最小的同时,字典序最大的解(即先要满足最后一个数最小;如果有多组解,则使得第一个数尽量大;如果仍有多组解,则使得第二个数尽量大,依次类推……)。

输入输出格式

输入格式:
共一行,为初始的数字。

输出格式:
共一行,为拆分之后的数列。每个数之间用逗号分隔。行尾无逗号。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#if 0
Writers: G.S.M. && Goes
#endif
const int N=505;
const int INF=2e9+5;
char s[N];int a[N],n;
/*
题解 :
第一步先求出最后的那个数最小为多少。
(T(i,j)表示从原数列下标i取到j的数字组成的数)
正向dp一次,dp1[i]表示前i个数字分成
任意多个递增数且最后的数最小时,
最后的数为T(dp1[i],i)。
则dp1[i]=max(j),(T(dp1[j-1],j-1)<T(j,i))。
第二步要求最后一个数确定的情况下,前面的数字按字典序尽
*/
bool pd(int l1,int r1,int l2,int r2){
    while(l1<=r1&&a[l1]==0) l1++;int len1=r1-l1+1;
    while(l2<=r2&&a[l2]==0) l2++;int len2=r2-l2+1;
    if(len1==0||len2==0) return false;
    if(len1<len2) return true;if(len1>len2) return false;
    for(int i=0;i<len1;i++){
        if(a[l1+i]<a[l2+i]) return true;
        if(a[l1+i]>a[l2+i]) return false;
    }return false;
}
int d[N],f[N];
inline void gsdp1(){
    for(int i=1;i<=n;i++){d[i]=1;
        for(int j=i;j>=1;j--)
        if(pd(d[j-1],j-1,j,i)){
            d[i]=j;break;}
    }return ;
}
inline void gsdp2(){
    f[d[n]]=n;int zero=d[n];
    while(!a[zero-1]) f[zero-1]=n,zero--;
    for(int i=d[n]-1;i>=1;i--)
    for(int j=d[n]-1;j>=i;j--)
    if(pd(i,j,j+1,f[j+1])){f[i]=j;break;}
}
int main()
{
    scanf("%s",s+1);n=strlen(s+1);
    for(int i=1;i<=n;i++)a[i]=s[i]-'0';
    gsdp1();gsdp2();int pos=1,flag=0;
    while(pos<=n){
        if(flag) putchar(',');flag=1;
        for(int i=pos;i<=f[pos];i++) 
        printf("%d",a[i]);pos=f[pos]+1;
    }return 0;
}
### 洛谷 P2947 向右看齐 Python 解法 洛谷 P2947 题目要求找出每头奶牛向右看时,第一个比它高的奶牛的位置。如果不存在,则输出 0。此问题可以通过单调栈来高效解决,时间复杂度为 O(n),适用于较大的输入规模。 单调栈的核心思想是维护一个栈,栈中保存的是奶牛的索引,并且这些索引对应的奶牛高度是单调递减的。从右往左遍历数组,每次比较当前奶牛的高度与栈顶元素的高度,如果当前奶牛的高度大于等于栈顶元素的高度,则将栈顶元素弹出,直到栈为空或者栈顶元素的高度更高为止。此时,如果栈为空,则说明当前奶牛右侧没有更高的奶牛,否则栈顶元素的索引即为当前奶牛向右看的第一个更高的奶牛的位置。 以下是洛谷 P2947 的 Python 解法实现: ```python def main(): import sys input = sys.stdin.read data = input().split() n = int(data[0]) heights = list(map(int, data[1:n+1])) stack = [] result = [0] * n for i in range(n - 1, -1, -1): # 弹出栈顶比当前奶牛矮或相等的元素 while stack and heights[stack[-1]] <= heights[i]: stack.pop() if stack: result[i] = stack[-1] + 1 # 题目要求位置从1开始计数 else: result[i] = 0 stack.append(i) print('\n'.join(map(str, result))) if __name__ == "__main__": main() ``` 在上述代码中,使用了 `sys.stdin.read` 来一次性读取所有输入数据,这在处理大量输入时效率更高。读取输入后,将数据拆分为奶牛的数量 `n` 和每头奶牛的高度列表 `heights`。 定义了一个空栈 `stack` 用于保存奶牛的索引,并且定义了一个结果列表 `result`,初始值为 0。然后从右往左遍历奶牛的高度列表 `heights`,每次遍历中,弹出栈顶比当前奶牛矮或相等的元素,确保栈顶元素是第一个比当前奶牛高的元素。如果栈不为空,则栈顶元素的索引加 1(因为题目要求位置从 1 开始计数)即为当前奶牛向右看的第一个更高的奶牛的位置;如果栈为空,则当前位置的值保持为 0。最后将当前奶牛的索引压入栈中,继续下一次循环。 该算法的时间复杂度为 O(n),因为每个元素最多被压入和弹出栈一次。空间复杂度也为 O(n),主要取决于栈的大小和结果列表的大小[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GoesM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值