NOIP2000TG T2 乘积最大

题目链接

 

题意:

 将$N(6<=N<=40)$位的数字,用$K(1<=K<=6)$个乘号隔开,求最大积。

 

程序1(0pt):

看到分割/合并,考虑区间DP,思考一下,因为区间个数有限制,不能套区间DP乱搞。

因为乘法满足结合律,考虑将“摸到第$i$位”和“在这一位后放置第$j$个乘号”作为两个维度进行DP,记录最大值,设为$f_{i,j}$。

首先设给定数串为$a$,取其一部分的函数为$cut(a,l,r)$,发现从高位和低位开始取是一样的,为了简化,就和高精度一样,以最低位为第$1$位。

每次用第一个乘号时产生初值,每次从$j-1$的状态里转移。

同时,因为只能从计算过的状态转移才有意义,并且为了避免做高精乘法的时候出现不可知的奇怪错误,所以考虑在实现DP的时候维护这个访问值,设为$v$。

考虑寻找上一个节点的位数$p$满足

①$p>=j-1$,因为这里要放置第$j-1$个乘号,至少为第$j-1$位

②$p<i$,从前往后转移

③$f_{p,j-1}必须被访问过$

于是我们有

$$f_{i,j}=\mathop{max}\limits_{p=j-1}^{i-1}\{([v_{p,j-1}]*f_{p,j-1}*cut(a,p+1,i)\}$$

跑完$f_{i}$之后,显然$f_{i,k}$就是答案候选了。

然而有一个问题,就是读入。遇到这种数字和字串混合的输入很容易被爆。第一次提交的时候就因为读入爆〇了。好尴尬啊。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=40;
const int M=6;

struct num{
    int s[N+3],l;
    
    num(int x){
        memset(s,0,sizeof s);
        
        if(x==0){
            l=1;
            return ;
            
        }
        
        l=0;
        while(x>0){
            s[++l]=x%10;
            x/=10;
            
        }
        
    }
    
    num(){}
    
    int operator[](int i){
        return s[i];
        
    }
    
};

void prt(num a){
    for(int i=a.l;i>=1;i--)
        putchar(a[i]+'0');
    
}

num operator*(num a,num b){
    num c=num(0);
    
    for(int i=1;i<=a.l;i++)
        for(int j=1;j<=b.l;j++)
            c.s[i+j-1]+=a[i]*b[j];
    
    c.l=a.l+b.l+1;
    for(int i=1;i<c.l;i++){
        c.s[i+1]+=c[i]/10;
        c.s[i]%=10;
        
    }
    while(c[c.l]==0)
        c.l--;
        
    return c;
    
}

bool operator<(num a,num b){
    if(a.l!=b.l)
        return a.l<b.l;
    
    for(int i=a.l;i>=1;i--)
        if(a[i]!=b[i])
            return a[i]<b[i];
    return false;
    
}

num max(num a,num b){
    if(a<b)
        return b;
    return a;
    
}

num cut(num a,int l,int r){
    num b(0);    b.l=r-l+1;
    for(int i=1;i<=b.l;i++)
        b.s[i]=a[l+i-1];
    return b;
    
}

    num a;
    int n,k;
    num f[N+3][M+3],ans[N+3];
    bool v[N+3][M+3];
    

int main(){
    scanf("%d%d",&n,&k);getchar();
    char ch;    a.l=n;
    for(int i=n;i>=1;i--){
        ch=getchar();
        a.s[i]=ch-'0';
        
    }
    
    memset(v,0,sizeof 0);
    for(int i=1;i<n;i++){            
        f[i][1]=cut(a,1,i);
        v[i][1]=true;
        
        for(int j=2;j<=k;j++)        
            for(int p=j-1;p<i;p++)    
            if(v[p][j-1]){
                v[i][j]=1;
                f[i][j]=max(f[i][j]
                    ,f[p][j-1]*cut(a,p+1,i));
                
            }
            
        if(v[i][k])    
            ans[i]=f[i][k]*cut(a,i+1,n);
    
    num maxa(0);
    for(int i=1;i<n;i++)
    if(v[i][k])
        maxa=max(maxa,ans[i]);
    prt(maxa);
    
    return 0;
    
}

 

 

 

程序2(100pt):

思考了一下,应该是$scanf$和$getchar$混在同一行,那个回车不知何去何从导致的。其实我现在还没太搞明白个中机制。

考虑用模式串把回车收起来。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=40;
const int M=6;

struct num{
    int s[N+3],l;
    
    num(int x){
        memset(s,0,sizeof s);
        
        if(x==0){
            l=1;
            return ;
            
        }
        
        l=0;
        while(x>0){
            s[++l]=x%10;
            x/=10;
            
        }
        
    }
    
    num(){}
    
    int operator[](int i){
        return s[i];
        
    }
    
};

void prt(num a){
    for(int i=a.l;i>=1;i--)
        putchar(a[i]+'0');
    
}

num operator*(num a,num b){
    num c=num(0);
    
    for(int i=1;i<=a.l;i++)
        for(int j=1;j<=b.l;j++)
            c.s[i+j-1]+=a[i]*b[j];
    
    c.l=a.l+b.l+1;
    for(int i=1;i<c.l;i++){
        c.s[i+1]+=c[i]/10;
        c.s[i]%=10;
        
    }
    while(c[c.l]==0)
        c.l--;
        
    return c;
    
}

bool operator<(num a,num b){
    if(a.l!=b.l)
        return a.l<b.l;
    
    for(int i=a.l;i>=1;i--)
        if(a[i]!=b[i])
            return a[i]<b[i];
    return false;
    
}

num max(num a,num b){
    if(a<b)
        return b;
    return a;
    
}

num cut(num a,int l,int r){
    num b(0);    b.l=r-l+1;
    for(int i=1;i<=b.l;i++)
        b.s[i]=a[l+i-1];
    return b;
    
}

    num a;
    int n,k;
    num f[N+3][M+3],ans[N+3];
    bool v[N+3][M+3];
    

int main(){
    scanf("%d%d\r",&n,&k);
    char ch;    a.l=n;
    for(int i=n;i>=1;i--){
        ch=getchar();
        a.s[i]=ch-'0';
        
    }
    
    memset(v,0,sizeof 0);
    for(int i=1;i<n;i++){                
        f[i][1]=cut(a,1,i);
        v[i][1]=true;
        
        for(int j=2;j<=k;j++)                
            for(int p=j-1;p<i;p++)                
            if(v[p][j-1]){
                v[i][j]=1;
                f[i][j]=max(f[i][j]
                    ,f[p][j-1]*cut(a,p+1,i));
                
            }
            
        if(v[i][k])                        
            ans[i]=f[i][k]*cut(a,i+1,n);        
    
    num maxa(0);
    for(int i=1;i<n;i++)
    if(v[i][k])
        maxa=max(maxa,ans[i]);
    prt(maxa);
    
    return 0;
    
}

 

 

小结:

要命的字符串搞法……

 

posted @ 2019-06-08 09:09  汉谡  阅读(186)  评论(0编辑  收藏  举报