【2020HDU多校六:区间dp】Fragrant numbers

HDU6831 : Fragrant numbers

【难度】

3.5 / 10 3.5/10 3.5/10
其实很简单的区间dp
但是当时没有时间想就没做出来//

【题意】

S S S是一个无限的串“1145141919”拼接在一起,成为 “ 114514191911451419191145141919 ⋯ ” “114514191911451419191145141919\cdots” 114514191911451419191145141919
选择 S S S 的一个前缀 T T T,你可以在 T T T 中插入 ′ ( ′ ′ ) ′ ′ + ′ ′ ∗ ′ '('\quad ')'\quad '+'\quad '*' ()+符号,形成新的串 T ′ T' T
v a l ( T ′ ) val(T') val(T) 的值符合十进制的一般运算规则
现在给你一个数字 N N N,让你求出最小长度的 T , s . t . v a l ( T ′ ) = N T,s.t.\quad val(T')=N Ts.t.val(T)=N
如果没有这样的串 T T T ,则输出 -1

【数据范围】

1 ≤ N ≤ 5000 1\le N\le 5000 1N5000

【样例输入】

T
N

3
520
1
2

【样例输出】

6
1
2

【解释】

N=520时:
T = 114514 , T ′ = 1 + 1 + 4 + 514 , v a l ( T ′ ) = 520 T=114514,T'=1+1+4+514,val(T')=520 T=114514,T=1+1+4+514,val(T)=520

【思路】

一般看到数据范围特别小的题,都是可以打表处理的。
既然想到里面括号可以任意添加,那么对于两个相邻的串 S [ i , k ] 与 S [ k + 1 , j ] S[i,k]与S[k+1,j] S[i,k]S[k+1,j]
S [ i , k ] S[i,k] S[i,k]能计算出答案 v a l S [ i , k ] ∈ A val_{S[i,k]}\in\mathbb{A} valS[i,k]A
S [ k + 1 , j ] S[k+1,j] S[k+1,j]能计算出答案 v a l S [ k + 1 , j ] ∈ B val_{S[k+1,j]}\in\mathbb{B} valS[k+1,j]B

那么容易得到
S e t [ i , j ] = { v a l ∣ v a l = p + q o r v a l = p × q   , 其 中 p ∈ A   并 且   q ∈ B } Set[i,j]=\{val \mid val=p+q \quad or\quad val=p\times q\,,其中p\in\mathbb{A} \,并且\,q\in \mathbb{B}\} Set[i,j]={valval=p+qorval=p×q,pAqB}

其他一些细节处理:

注意对于一个区间 [ i , j ] [i,j] [i,j] 会存在多个不同的值,用 s e t \pmb{set} setsetset 存储。
注意数字大于5000时,因为没有减号操作,因此数字不会变小,便不要插入到集合中去

【预处理代码】

这里 n n n 表示枚举的S数组的长度,不表示题目所给的 N N N
时间复杂度 o ( n 3 × ∣ S e t [ i , k ] ∣ × ∣ S e t [ k + 1 , j ] ∣ 1   ≤   i ≤   k ≤   j ≤   n ) o(n^3\times\underset{1\,\le \,i\le \,k\le \,j\le \,n}{\Big| Set[i,k]\Big|\times\Big |Set[k+1,j] \Big |}) o(n3×1ikjnSet[i,k]×Set[k+1,j])
我这里选 n = 20 n=20 n=20 基本跑一分钟也能跑完

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 20;			///其实只要枚举S串长12即可,稍微往大开

set<int>S[MAX][MAX];			///区间DP的set
int fir[5050];				///答案数组
int tmp[MAX];    			///恶臭的S数组

void init(){
    for(int i=1;i<=5000;++i)fir[i]=INF;
    
    tmp[1]=tmp[2]=tmp[5]=tmp[7]=tmp[9]=1;
    tmp[3]=tmp[6]=4;
    tmp[4]=5;
    tmp[8]=tmp[10]=9;
    for(int i=11;i<MAX;++i)tmp[i] = tmp[i-10];      	///恶臭的S数组循环一下
    
    for(int L=1;L<=4;++L){				///首先处理区间S[i,j]不加运算符的值
        for(int i=1;i<MAX-L+1;++i){
            int j = i + L - 1;
            int t = 0;
            for(int k=i;k<=j;++k){
                t*=10;
                t+=tmp[k];
            }
            if(t>5000)continue;
            S[i][j].insert(t);
            if(i==1)fir[t]=min(fir[t],j);
        }
    }
}
int main(void){
    init();
    for(int L=2;L<MAX;++L){				///区间DP 枚举区间长度
        for(int i=1;i<MAX-L+1;++i){    			///区间DP 枚举区间起点
            int j = i + L - 1;				/// 重点
            for(int k=i;k<j;++k){			/// 枚举两个区间的分界
                for(auto &It_i : S[i][k]){
                    for(auto &It_j : S[k+1][j]){
                        int add = It_i + It_j;
                        int mul = It_i * It_j;
                        if(add<=5000){			/// 注意剪枝
                            S[i][j].insert(add);
                            if(i==1)fir[add]=min(fir[add],j);	/// 只有i=1才是前缀答案
                        }
                        if(mul<=5000){  		 /// 注意剪枝
                            S[i][j].insert(mul);
                            if(i==1)fir[mul]=min(fir[mul],j);
                        }
                    }
                }
            }
        }
    }
    for(int i=1;i<=5000;++i){				/// 无解应该输出-1 输出在文件中即可打表完成
        printf("%d,",fir[i]==INF ? -1 : fir[i]);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值