【BZOJ2239】【C++心路历程38】猜谜语【dp线型序列分组计算】

【问题描述】

  给出一个长度为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.
【分析】
初看数据很小啊,搜索: 3^N,能过50分,再加优化就不会了。。于是用dp。
1.
f(i,j) 表示前i个元素 和为j 需要的最少符号数;(用加号来分组)
f(i,j)=MIN{f(k,x)+g[1+k][i][j-x] } (1<=k< i && j-x>=0)
2.
组权值数组g[i][j][k]的意义:
在i到j的区间内 乘积为k所需要的最小乘号数
g[i][j][k]=MIN{g[i][p][y]+1 ||i<= p < j && y*num[p+1][j]==k}

3.
num[x][y] 表示x到y区间内的一个整数 (即表示的数)

所以先算num,再算g,再算f。

//num[x][y] 表示x到y区间内的一个整数 
void ready()
{
    for(int i=1;i<=n;i++)
    {
        num[i][i]=a[i]-'0';
        for(int j=i+1;j<=n;j++)
        {
            num[i][j]=num[i][j-1]*10+a[j]-'0';
            if(num[i][j]>T) num[i][j]=T+1;
        }
    }
}
//组权值数组g[i][j][k]
void dp1()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=i;j<=n;j++)
        {
            g[i][j][num[i][j]]=0;
            for(int k=0;k<=T;k++)
            {
                if(k==num[i][j]) continue;
                g[i][j][k]=inf;
            }
        }   
    }
    for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)
    for(int k=0;k<=T;k++)
    {
        if(k==num[i][j]) {g[i][j][k]=0;continue;}
        int tt=inf*2;
        for(int p=i;pint e=num[p+1][j];
            if(e==0)
            {
                if(k==0){tt=0;break;}
                else continue;
            }
            else 
            {
                if(k%e==0)
                {
                    int y=k/e;tt=min(tt,g[i][p][y]);
                }
            }
        }
        g[i][j][k]=tt+1;
    }

}
//f(i,j) 表示前i个元素 和为j 需要的最少符号数;
void dp2()
{   
    for(int i=1;i<=n;i++)
    for(int j=0;j<=T;j++)
    {
        d[i][j]=g[1][i][j];
    }
    for(int i=1;i<=n;i++)
    for(int j=0;j<=T;j++)
    {
        int tt=inf*2;
        for(int k=1;kfor(int x=0;x<=j;x++)
        {
            tt=min(tt,d[k][x]+g[k+1][i][j-x]+1);
        }
        d[i][j]=min(d[i][j],tt);
    }
    if(d[n][T]>=inf) printf("-1\n");
    else cout<

“`

你可能感兴趣的:(【BZOJ2239】【C++心路历程38】猜谜语【dp线型序列分组计算】)