【问题描述】
给出一个长度为N的数字字符串和一个数字T,要求插入最少的加号或者乘号,使得数字字符串的运算结果为T。运算符*号优先级高于+号,运算数可以有任意个前导0.
【输入格式】
输入不超过5组数据,每组数据两行。
每组数据的第一行为长度N,只包含0~9的数字字符串;第二行为一个数字T。
输入T<0表示输入结束。
【输出格式】
输出一个数字单独占一行,表示最少需要添加的运算符(+号或*号)数,无解输出-1.
【输入样例】
032089
5
333
9
00
-1
【输出样例】
3
2
【数据范围】
对于30%的数据,有1<=N<=10,0<=T<=50.
对于50%的数据,有1<=N<=15,0<=T<=200.
对于全部的数据,有1<=N<=20,0<=T<=300.
看起来就很恶心的题目:的优先级>+,所以可以把用连接的连续数字看成一组,分组动归。
状态方程:f[i][j]表示前i个数字和为j,添加的运算符的最小个数
f[i][j]= min{ f[k][x] +1 +g[k+1][i][j-x] | 1<=k< i && j-x>=0}
边界:f[i][j]=g[1][i][j]
g[i][j][x]表示在第i~j个数字之间插入*,得到x的最少插入值<组权值>
g[i][j][x]=min{ g[i][p][k/num[p+1][j]] +1 | k%num[p+1][j]==0}
边界:g[i][j][num[i][j]]=0 |num[i][j]<=T
num[i][j]表示第i~j个数字组成一个新的数字是多少(可以有前导0)
num[i][j]=num[i][j-1]+s[j]-‘0’
实现发现问题了:
1.num数组中,若i< j,则num[i][j]的值不应为0,而是-1,如果是0的话算g的时候有问题(不过好像循环计算g[i][j][k]时保证j>=i的话也没有问题,代码中没有用num[i][j]=max(0,num[i][j-1])*10+s[j]-‘0’ );
num[i][j]可能>long long ,那也没办法,就是负数,不过应该也不存在满足的,因为T<=300,不过在判断g[i][j][num[i][j]]=0的条件应该是 if(num[i][j]<=T && num[i][j]>=0);
2.循环到多大的值一点要注意;
3.f[i][j]的边界不是简单的inf,而是g[1][i][j],如果赋成inf则答案为-1;
4.关于g[i][j][0]的问题在代码中有写,卡了挺久的;
代码如下:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
const int maxn=25;
const int inf=100000000;
const int maxm=305;
int T,f[maxn][maxm],n,g[maxn][maxn][maxm];
char s[maxn];
int num[maxn][maxn];
void db()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=1;k<=T;k++) cout<<g[i][j][k]<<' ';
cout<<endl;
}
cout<<endl;
}
}
/*
f[i][j]表示前i个数字和为j的最小插入数
f[i][j]=min{ f[k][x]+1+g[k+1][i][j-x] | 1<=k<i && j-x>=0}
边界:f[i][j]=g[1][i][j]
g[i][j][k]表示用*把第i~j个数字连在一起乘积为k的最小代价
g[i][j][k]=min{ g[i][p][k/num[p+1][j]] +1 | k%num[p+1][j]==0}
注意每次g[i][j][0]可以提出来特判因为不能%0
边界:g[i][j][num[i][j]]=0 |num[i][j]<=T
*/
void dp()
{
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
{
for(int k=0;k<=T;k++) g[i][j][k]=inf;
if(num[i][j]<=T && num[i][j]>=0) g[i][j][num[i][j]]=0;//位数实在太多了爆LL没有办法,不完美
}
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
{
if(num[i][j]==0) g[i][j][0]=0;
else
{
if(num[i][i]==0 || num[j][j]==0) g[i][j][0]=min(g[i][j][0],1);
else
{
for(int p=i+1;p<j;p++) g[i][j][0]=min(g[i][j][0],g[i][p][0]+1);
}
}
for(int k=1;k<=T;k++)
{
for(int p=i;p<j;p++)
if(num[p+1][j]>0 && k%num[p+1][j]==0)
{
g[i][j][k]=min(g[i][p][k/num[p+1][j]]+1,g[i][j][k]);
}
}
}
for(int i=1;i<=n;i++)
for(int j=0;j<=T;j++) f[i][j]=inf;
for(int i=1;i<=n;i++)
for(int j=0;j<=T;j++)
{
int t=f[i][j];
for(int k=1;k<i;k++)
for(int x=0;x<=j;x++) t=min(t,f[k][x]+1+g[k+1][i][j-x]);
f[i][j]=t;
}
// db();
}
int main()
{
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
while(scanf("%s",s+1)!=EOF)
{
n=strlen(s+1);
memset(num,-1,sizeof(num));
scanf("%d",&T);
if(T<0) break;
for(int i=1;i<=n;i++)
{
num[i][i-1]=0;
for(int j=i;j<=n;j++)
{
num[i][j]=num[i][j-1]*10+s[j]-'0';
if(num[i][j]>T) break;
}
}
dp();
if(f[n][T]<inf) printf("%d\n",f[n][T]);
else printf("-1\n");
}
return 0;
}