0
点赞
收藏
分享

微信扫一扫

洛谷P1164 小A点菜

这道题对于还是菜鸟的我来说过于抽象了
将菜从1到n依次排开,每道菜只有点和不点两种情况
用f[i][j]表示前i个菜花费了j元
那对于第i个菜,有两种情况:
j<a[i];这种情况肯定没法点第i个菜,所以f[i][j]=f[i-1][j]
j>=a[i];而这种情况就可以点或不点了,即f[i][j]=f[i-1][j]+f[i-1][j-a[i]]
用两层循环,从1到n,从1到m,依次求出所有解
【注意到抛开j,全是i和i-1之间的关系,这就意味着待会儿可以降维,但降维时要注意j是从后往前更新的,因为我们只是简化,本质还是上面的公式,即是与上一层j的关系,如果从前更新的话,那f[j-a[i]]就是刚刚才更新过的数据,相当于f[i][j-a[i]]了】
即使原理,递推公式大概知道啥意思,但还是感觉理解太抽象了,还是画图帮助理解
在这里插入图片描述
其实真正理解后就会发现方法很神奇,原本感觉很复杂的情况,很多种可能(比如怎么让点的菜加起来正好等于m,无法确定选哪几个菜,每个菜好像都可以点或不点)竟然用两个式子就全部解决了

int main() {
    int n,m;
    cin>>n>>m;
    int a[105];
    for(int i=1;i<=n;i++) cin>>a[i];
    int f[105][10005]={0};
    
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            if(j<a[i]) f[i][j]=f[i-1][j];
            if(j==a[i]) f[i][j]=f[i-1][j]+1;//所有的f[i][0]=1
            if(j>a[i]) f[i][j]=f[i-1][j]+f[i-1][j-a[i]];
        }
    }
    cout<<f[n][m];
    //降维
    int f[10005]={0};
    
    for(int i=1;i<=n;i++) {
        for(int j=m;j>=a[i];j--) {
       //一定要倒着更新,
            if(j==a[i]) f[j]++;//f[0]=1
            if(j>a[i]) f[j]+=f[j-a[i]];
       //想了半天才发现就是因为这个“j-a[i]”是在前面
        }
    }
    cout<<f[m];
    return 0;
        
}
举报

相关推荐

0 条评论