P1415 拆分数列(记忆化搜索)

这么麻烦的题敲出来没WA真的是舒服~

原题: https://www.luogu.org/problemnew/show/P1415

题意:

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

解析:


大小比较函数:

int compare(int ll,int rr,int l,int r){
    while(ll<rr&&x[ll]=='0')ll++;
    while(l<r&&x[l]=='0')l++;
    if(rr-ll>r-l)return 1;
    if(rr-ll<r-l)return -1;
    for(;ll<=rr;ll++,l++){
        if(x[ll]>x[l])return 1;
        if(x[ll]<x[l])return -1;
    }
    return 0;
}

第一要求最后一个数最小,那么先从后往前,得到一个最小的满足要求的段。这个段只要可以做到满足要求,那么答案段就是这个。(这个串的前面可能有前缀0)

这个过程需要用到记忆化搜索,即L~R这个区间可能被搜索多次,如果这个区间不能达到要求,那么标记-剪枝。


找到最后一段后,再要求前面的数大。那么枚举从大开始,1~L-11~1,如果满足要求则打标记,ans[i]=1表示第i个字母后面插逗号。

往后搜的过程也需要记忆化搜索-打标记,我第一发忘记vis[l][r]=1;就TLE了。


细节:

  1. 一个段全是0也判不符合,要求打标记。
  2. 最后一段L==1则直接输出即可。
  3. 最后一段L前面可能有前缀0,所以往后搜的结束标志为:右边界落在为0的那个区间内。
#include<bits/stdc++.h>
using namespace std;

char x[509];
int len;

int compare(int ll,int rr,int l,int r){
    while(ll<rr&&x[ll]=='0')ll++;
    while(l<r&&x[l]=='0')l++;
    if(rr-ll>r-l)return 1;
    if(rr-ll<r-l)return -1;
    for(;ll<=rr;ll++,l++){
        if(x[ll]>x[l])return 1;
        if(x[ll]<x[l])return -1;
    }
    return 0;
}

bool isempty(int l,int r){
    for(int i=l;i<=r;i++)if(x[i]!='0')return 0;
    return 1;
}

bool vis[509][509];//记忆化
bool check(int L,int R){
    if(isempty(L,R)){vis[L][R]=1;return 0;}
    if(L==1)return true;
    for(int i=L-1;i>=1;i--){
        if(vis[i][L-1])continue;
        if(compare(i,L-1,L,R)<0){
            if(check(i,L-1))return 1;
        }
        else break;//数变大就更不行了
    }
    vis[L][R]=1;//标记
    return 0;
}

int L,LL;
bool ans[509];

bool check2(int l,int r){
    if(vis[l][r])return 0;
    if(isempty(l,r)){vis[l][r]=1;return 0;}
    if(compare(l,r,L,len)>=0){vis[l][r]=1; return 0;}
    
    //搜索结束
    if(r>=LL-1&&r<=L-1){ans[r]=1;return 1;}

    for(int i=L-1;i>r;i--){
        if(compare(r+1,i,L,len)>=0){
            vis[r+1][i]=1;continue;
        }
        if(compare(r+1,i,l,r)<=0)continue;
        if(check2(r+1,i)){
            ans[i]=1;return 1;
        }
    }
    vis[l][r]=1;//标记
    return 0;
}

int main(){
    scanf("%s",x+1);
    len=strlen(x+1);
    L=1;
    
    for(L=len;L>=1;L--){
        if(check(L,len)){break;}
    }
    if(L==1)return 0*printf("%s\n",x+1);

    LL=L;while(LL>1&&x[LL-1]=='0')LL--;

    memset(vis,0,sizeof(vis));
    for(int i=L-1;i>=1;i--){
        if(check2(1,i)){
            ans[i]=1;break;
        }
    }
    for(int i=1;i<=len;i++){
        printf("%c",x[i]);
        if(ans[i])printf("%c",',');
    }
    printf("\n");
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/87183130