题目描述给出一个长度为N的数字字符串和一个数字T,要求插入最少的加号或者乘号,使得数字字符串的运算结果为T。运算符*号优先级高于+号,运算数可以有任意个前导0.
输入输入不超过5组数据,每组数据两行。每组数据的第一行为长度N,只包含0~9的数字字符串;第二行为一个数字T。输入T<0表示输入结束。
输出输出一个数字单独占一行,表示最少需要添加的运算符(+号或*号)数,无解输出-1.
样例输入123123000000000000000000992299400320895333900-1
样例输出00532
提示对于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; }
[BZOJ2239]猜谜语
最新推荐文章于 2018-03-09 16:16:17 发布