快速求和

快速求和

給定一個數字字符串,用最少次數的加法讓字符串等於一個給定的目標數字。每次加法就是在字符串的某個位置插入一個加號。在需要的所有加號都插入後,就象做普通加法那樣來求值。 例如,考慮字符串"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");
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章