快速求和

快速求和

给定一个数字字符串,用最少次数的加法让字符串等于一个给定的目标数字。每次加法就是在字符串的某个位置插入一个加号。在需要的所有加号都插入后,就象做普通加法那样来求值。 例如,考虑字符串"12",做0次加法,我们得到数字12。如果插入1个加号,我们得到3。因此,这个例子中,最少用1次加法就得到数字3。 再举一例,考虑字符串"303"和目标数字6,最佳方法不是"3+0+3",而是"3+03"。能这样做是因为1个数的前导0不会改变它的大小。 写一个程序来实现这个算法。

Input

第1行:1个字符串S(1<=length(S)<=20)和1个整数N(N<=2^9-1)。S和N用空格分隔。

Output

第1行:1个整数K,表示最少的加法次数让S等于N。如果怎么做都不能让S等于N,则输出-1。

Sample Input

2222 8

Sample Output

3

Analysis

首先,考虑深度优先搜索,枚举每一个位置,可以选择在那里放加号或不放。
放加号时,就相当于把这个数分为前后两个部分,所以我们考虑预处理,将每一段用数组存起来。

for(int i=1;i<=len;i++)//len为输入字符串长度
    g[i][i]=s[i-1]-'0';//从第i位到第i位就是这一位本身
for(int i=1;i<len;i++)//枚举起始位置
    for(int j=i+1;j<=len;j++)//枚举终点
    {
        g[i][j]=g[i][j-1]*10+g[j][j];//从i到j组成的就是从i到j-1的数*10+j这一位
        if(g[i][j]>INF)g[i][j]=INF;//INF为定义的极大值
        //数据有可能超出范围,当超出极大值时,就肯定不能加上一个数组成n,就赋成极大值
    }

所以就开始写深度优先搜索的函数

void dfs(int x,int last,int p,int sum)
//x为当前位数,laxt为上一次放加号的位置的前一位,p为已放加号的个数,sum为目前的和
{
    if(x==len)//当找到最后一位时
    {
        sum+=g[last+1][x];//由于这一位还没加,所以加上
        if(sum==n&&p<ans)ans=p;//刷新ans的值
        return;//返回(很重要)
    }
    dfs(x+1,last,p,sum);//不加加号
    int t=g[last+1][x];
    dfs(x+1,x,p+1,sum+t);//加上加号,把上一个阶段的数加到总和中
}

代码如下

#include<cstdio>
#include<cstring>
const int INF=100000000;
char s[25];
int n,len,g[25][25],ans=INF;
void read()
{
    scanf("%s%d",s,&n);
    len=strlen(s);
    for(int i=1;i<=len;i++)
        g[i][i]=s[i-1]-'0';
    for(int i=1;i<len;i++)
        for(int j=i+1;j<=len;j++)
        {
            g[i][j]=g[i][j-1]*10+g[j][j];
            if(g[i][j]>INF)g[i][j]=INF;
        }
}
void dfs(int x,int last,int p,int sum)
{
    if(x==len)
    {
        sum+=g[last+1][x];
        if(sum==n&&p<ans)ans=p;
        return;
    }
    dfs(x+1,last,p,sum);
    int t=g[last+1][x];
    dfs(x+1,x,p+1,sum+t);   
}
int main()
{
    read();
    dfs(1,0,0,0);
    if(ans!=INF)printf("%d\n",ans);
    else printf("-1\n");
    return 0;
}

就算这样,耗时也高,所以我们采用剪枝方法优化
1.当当前阶段sum的值累加已经超过n时就可以直接返回上一阶段,因为这是做加法。
2.当目前使用加号已超过ans的值时,也返回。

#include<cstdio>
#include<cstring>
const int INF=100000000;
char s[25];
int n,len,g[25][25],ans=INF;
void read()
{
    scanf("%s%d",s,&n);
    len=strlen(s);
    for(int i=1;i<=len;i++)
        g[i][i]=s[i-1]-'0';
    for(int i=1;i<len;i++)
        for(int j=i+1;j<=len;j++)
        {
            g[i][j]=g[i][j-1]*10+g[j][j];
            if(g[i][j]>INF)g[i][j]=INF;
        }
}
void dfs(int x,int last,int p,int sum)
{
    if(sum+g[last+1][x]>n)return;//大于n
    if(p>=ans)return;//大于ans
    if(x==len)
    {
        sum+=g[last+1][x];
        if(sum==n&&p<ans)ans=p;
        return;
    }
    dfs(x+1,last,p,sum);
    int t=g[last+1][x];
    dfs(x+1,x,p+1,sum+t);   
}
int main()
{
    read();
    dfs(1,0,0,0);
    if(ans!=INF)printf("%d\n",ans);
    else printf("-1\n");
    return 0;
}

接下来,看一道加强版。(题目描述一样,数据不同)

Input

第1行:1个字符串S(1<=length(S)<=40)和1个整数N(N<=10^5)。S和N用空格分隔。

Output

第1行:1个整数K,表示最少的加法次数让S等于N。如果怎么做都不能让S等于N,则输出-1。

Sample Input

2222222222222222222222222222222222222222 80

Sample Output

39

/(ㄒoㄒ)/~~
X﹏X
看这个样子,用DFS不行,用BFS也不行,但我们可以用记忆化深搜

#include<cstdio>
#include<cstring>
const int MAXN=100,MAXV=100005;
char s[MAXN];
int n,len,memo[MAXN][MAXV],g[MAXN][MAXN];
//memo[i][j]表示从i到len组成j用的最少加号数
void read()//预处理还是一样
{
    scanf("%s%d",s,&n);
    len=strlen(s);
    for(int i=1;i<=len;i++)
        g[i][i]=s[i-1]-'0';
    for(int i=1;i<len;i++)
        for(int j=i+1;j<=len;j++)
        {
            g[i][j]=g[i][j-1]*10+g[j][j];
            if(g[i][j]>MAXV)g[i][j]=MAXV;
        }
}
int dfs(int x,int goal)//表示从x到len组成goal需要的最少加号数
{
    int t,nxt,minp=MAXN;
    if(x>len)//边界
    {
        if(!goal)return 0;
        else return MAXN;
    }
    if(memo[x][goal])return memo[x][goal];//已经计算过
    int &ans=memo[x][goal];//ans为memo的地址
    for(int i=x;i<=len;i++)//开始枚举
    {
        t=g[x][i];
        if(t>goal)break;//当这个数大于要凑的数时,就直接退出循环
        nxt=dfs(i+1,goal-t);
        if(nxt<minp)minp=nxt;
    }
    if(minp<MAXN)ans=minp+1;
    else ans=MAXN;//更新ans的值(实际更新了memo[x][goal]的值)
    return ans;
}
int main()
{
    read();
    int an=dfs(1,n);
    if(an!=MAXN)printf("%d\n",an-1);//因为默认了最开头有一个加号
    else printf("-1\n");
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章