[BZOJ2239]猜谜语

题目描述
给出一个长度为N的数字字符串和一个数字T,要求插入最少的加号或者乘号,使得数字字符串的运算结果为T。运算符*号优先级高于+号,运算数可以有任意个前导0.

输入
输入不超过5组数据,每组数据两行。
每组数据的第一行为长度N,只包含0~9的数字字符串;第二行为一个数字T。
输入T<0表示输入结束。

输出
输出一个数字单独占一行,表示最少需要添加的运算符(+号或*号)数,无解输出-1.

样例输入
123 
123 
00000000000000000 
992299 
40
032089
5
333
9
00
-1

样例输出
0
0
5
3
2

提示
对于30%的数据,有1<=N<=10,0<=T<=50.
对于50%的数据,有1<=N<=15,0<=T<=200.
对于全部的数据,有1<=N<=20,0<=T<=250.


题解:
分为两步: 
第一步处理只有乘号的情况: 
dp[i][j][k]: 从第i个数字, 到第j个数字, 运算结果为k时, 所需要添加的最少乘号个数。 
dp[i][j][k]=min( dp[i][j][k], dp[i][p][x]+1 )  ( i<=p<j, k整除x且x=k/num[p+1][j] ) 
num[p+1][j]为从第p+1个数字到第j个数字组成的数的值。 
初始值为dp[i][j][num[i][j]]=0 
注意num[p+1][j]为0时要特别处理, 因为乘法中乘一个0就是0了 
  
第二步处理有乘号和加号的情况: 
G[i][j]: 前i个字符, 运算结果为j时, 需要添加的最少的运算符个数。 
G[i][j]=min( G[i][j], G[k][j-x]+dp[k+1][i][x]+1 ) ( 0<=k<i, 0<=x<=j ) 
初始值G[i][j]=dp[0][i][j]


#include<iostream> 
#include<cstring> 
#include<cstdio> 
#include<cstdlib> 
#include<cmath> 
#include<algorithm> 
using namespace std;  
const int INF=0x3f3f3f3f;     
const int N=25;  
const int M=255;  
int n, T, num[N][N];  
char s[N]; 
int dp[N][N][M], G[N][M]; 
   
int main() { 
    for(;;) { 
        scanf( "%s%d", s+1, &T ); 
        if( T<0 ) break; 
        memset( num, -1, sizeof num ); 
        memset( dp, 0x3f, sizeof dp ); 
        memset( G, 0x3f, sizeof G ); G[0][0]=0; 
        n=strlen( s+1 ); 
          
        for( int i=1; i<=n; i++)  
            for( int j=i; j<=n; j++ ){  
                int tmp=0;  
                for( int k = i; k<=j; k++ )  
                    tmp=tmp*10+s[k]-'0';  
                num[i][j]=tmp;  
                if( 0<=tmp && tmp<=T) dp[i][j][tmp]=0;  
            } 
           
        for( int i=1; i<=n; i++ ) 
            for( int j=i; j<=n; j++ ) 
                for( int p=i; p<j; p++ ) { 
                    if( !num[p+1][j] ) dp[i][j][0]=min( dp[i][j][0], 1 ); 
                    dp[i][j][0]=min( dp[i][j][0], dp[i][p][0]+1 ); 
                    for( int k=1; k<=T; k++ ) 
                        if( num[p+1][j]>0 && !( k%num[p+1][j] ) ) 
                            dp[i][j][k]=min( dp[i][j][k], dp[i][p][ k/num[p+1][j] ]+1 ); 
                } 
              
        for( int i=1; i<=n; i++ ) 
            for( int j=0; j<=T; j++ ) 
                for( int k=0; k<i; k++ ) 
                    for( int x=0; x<=j; x++ ) 
                        G[i][j]=min( G[i][j], G[k][x]+dp[k+1][i][j-x]+1 ); 
          
        if( G[n][T]>=INF ) printf( "-1\n" ); 
        else printf( "%d\n", G[n][T]-1 ); 
    } 
    return 0; 
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值