zoukankan      html  css  js  c++  java
  • ZOJ-3662 Math Magic 背包DP

    这题不错,可惜我还是太弱了,没想到qwq。

    看了网上大佬题解之后写的,对比了一下代码,好像我写的还是挺简洁的(逃,只是吞行比较多)。

    因为直接用lcm的值做下标会超时,所以我们观察发现可以组成lcm为m的,其实只可能是m的因子。所以我们预处理出所有m的因子放到a数组里。然后开始DP:

    dp[i][j][k]代表选前i个数,和为j,lcm为a[k]的方案数。假设LCM(a,b)=c,因为知道a和c求b不容易,而知道a和b求c很容易,所以这里我们会采用刷表法。

    另外即使我们已经优化了,因为ZOJ卡时间比较紧,所以还得预处理任两个数的lcm。

    具体细节请看代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e3+10;
    const int MOD=1e9+7;
    int n,m,p,cnt;
    int a[N],lcm[N][N],dp[2][N][40];
    
    int gcd(int a,int b) { return b==0 ? a : gcd(b,a%b); }
    
    int main()
    {
        for (int i=1;i<=1000;i++)
            for (int j=1;j<=1000;j++)
                lcm[i][j]=i*j/gcd(i,j);
        while (scanf("%d%d%d",&n,&m,&p)==3) {
            cnt=0; for (int i=1;i<=m;i++) if (m%i==0) a[++cnt]=i;
            
            memset(dp,0,sizeof(dp));
            for (int i=1;i<=cnt;i++) dp[1][a[i]][i]=1;
            for (int i=1;i<p;i++) {  //填i个数 
                int now=i%2,nxt=now^1;
                memset(dp[nxt],0,sizeof(dp[nxt]));
                for (int j=1;j<=n;j++)  //前i个数和为j 
                    for (int k=1;k<=cnt;k++) {  //前i个数lcm为a[k] 
                        for (int t=1;t<=cnt;t++)  //下个位置(i+1)填a[t] 
                        if (j+a[t]<=n && lcm[a[k]][a[t]]<=m) {
                            int tmp=lower_bound(a+1,a+cnt+1,lcm[a[k]][a[t]])-a;
                            dp[nxt][j+a[t]][tmp]+=dp[now][j][k];
                            dp[nxt][j+a[t]][tmp]%=MOD;
                        }
                    }
            }
            cout<<dp[p%2][n][cnt]<<endl;
        }
        return 0;
    }
  • 相关阅读:
    白盒测试方法
    单元测试 集成测试 系统测试
    快慢指针原理和应用
    实例方法,类方法,静态方法区别
    查找算法
    排序算法整理
    Oracle sql developer 删表时遇到问题unique/primary keys in table referenced by foreign keys
    剑指 Offer 18. 删除链表的节点(简单)
    剑指 Offer 17. 打印从1到最大的n位数(简单)
    Cyberdebut的补题列表
  • 原文地址:https://www.cnblogs.com/clno1/p/10774448.html
Copyright © 2011-2022 走看看