POJ 1239-Increasing Sequences(LIS 分割成上升序列-两次DP)

Increasing Sequences

Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 3089   Accepted: 1179

Description

Given a string of digits, insert commas to create a sequence of strictly increasing numbers so as to minimize the magnitude of the last number. For this problem, leading zeros are allowed in front of a number.

Input

Input will consist of multiple test cases. Each case will consist of one line, containing a string of digits of maximum length 80. A line consisting of a single 0 terminates input.

Output

For each instance, output the comma separated strictly increasing sequence, with no spaces between commas or numbers. If there are several such sequences, pick the one which has the largest first value;if there's a tie, the largest second number, etc.

Sample Input

3456
3546
3526
0001
100000101
0

Sample Output

3,4,5,6
35,46
3,5,26
0001
100,000101

Source

East Central North America 2002

题目意思:

有一串数字,在它们之间加逗号来分隔,使之成为一个上升序列。
要求最后一个数尽可能的小,如果有多个满足要求,则使第一个数尽可能大;如果还有多个,则使第二个最大,依此类推。

解题思路:

两次DP
①从前往后DP,找出最后一个数的最小值,即最后一个数尽可能的小;
②从后往前DP,找出最前面一个数的最大值,即第一个数尽可能的大。
具体可以看注释,代码From。

#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
using namespace std;

#define Maxn 90
char sa[Maxn];
int dp[Maxn];

bool isgreater(int i,int j,int m,int n) //判断从i~j的串是否大于从m~n的串
{
    while(sa[i]=='0'&&i<=j)//去掉前导0
        i++;
    while(sa[m]=='0'&&m<=n)//去掉前导0
        m++;
    if(i>j) //前者为0
        return false;
    if(m>n) //后者为0
        return true;
    int a=j-i+1,b=n-m+1;//数字的长度,即数字的位数
    if(a>b)
        return true;
    else if(a<b)
        return false;
    else
    {
        for(int k=i,p=m; k<=j&&p<=n; k++,p++)//如果两个数字的位数相同,就逐位比较
        {
            if(sa[k]>sa[p])//前一个数字大于后一个
                return true;
            else if(sa[k]<sa[p])//后一个数字大于前一个
                return false;
        }
    }
    return false; //等于的情况
}
int main()
{
    while(scanf("%s",sa+1)!=EOF)//字符串下标从1开始读入
    {
        int n=strlen(sa+1);//字符串总长
        if(n==1&&sa[1]=='0') break;
        dp[1]=1;//dp[i]表示从第i位往前共长度最小为dp[i]组成一个数字时的情况
        for(int i=2; i<=n; i++)
        {
            dp[i]=i;
            for(int j=i-1; j>=1; j--)
                if(isgreater(j+1,i,j-dp[j]+1,j))
                {
                    dp[i]=i-j; //求出满足题意的最小长度,即最后一个数尽可能的小
                    break;//跳转到下一个i
                }
        }
        //然后从后往前,dp[i]表示在满足第一个条件的情况下,从i开始的最大长度
        int len=n-dp[n]+1;//len表示原字符串中去掉最后一个数的第一个字符的位置
        dp[len]=dp[n];//最后一个数的长度
        for(int i=len-1; i>=1; i--)
        {
            if(sa[i]=='0')//数有前导0
            {
                dp[i]=dp[i+1]+1;//该数字长度再加上前导0的长度
                continue;
            }
            for(int j=len; j>i; j--)//求出长度最大的
                if(isgreater(j,j+dp[j]-1,i,j-1))
                {
                    dp[i]=j-i;//使第一个数尽可能大
                    break;
                }
        }
        for(int i=1; i<=dp[1]; i++) //输出第一个数字
            putchar(sa[i]);
        int t=dp[1]+1;//第二个数字的起始位置
        while(t<=n)
        {
            putchar(',');
            for(int i=t; i<t+dp[t]; i++)
                printf("%c",sa[i]);
            t=t+dp[t];//转移到下一个数字的起始位置
        }
        putchar('\n');
    }
    return 0;
}


你可能感兴趣的:(C++,dp,poj,LIS,sequences,1239,Increasing)