鹤上听雷 2023-06-03 23:50 采纳率: 100%
浏览 28
已结题

【动态规划】超时优化

已解决。


```c++
#include<bits/stdc++.h>
using namespace std;

const int M=1e6+10;
int c[M*2],a[M],n,k,ans,maxn;
int s1[50],s2[50]; 

void f(int b)
{
    memset(s1,0,sizeof(s1));
    memset(s2,0,sizeof(s2));
    int len1=0,len2=0,kk=k;
    while(b)
        s1[++len1]=b%2,b/=2;
    while(kk)
        s2[++len2]=kk%2,kk/=2;
    int len=max(len1,len2);
    for (int i=1;i<=len/2;i++)
        swap(s1[i],s1[len-i+1]),swap(s2[i],s2[len-i+1]);
    int sum=0;
    for (int i=1;i<=len;i++)
        if (s2[i]==0)
            sum=sum*2+s1[i];
        else
        {
            int k1=(sum*2+s1[i])*1<<(len-i),k2=(sum*2+1+s1[i])*1<<(len-i);
            c[k1]++,c[k2]--;
            sum=sum*2+(s1[i]^1);
        }
    c[sum]++,c[sum+1]--;
}

int main()
{
    scanf("%d%d",&n,&k);maxn=k;
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        f(a[i]);
        maxn=max(maxn,a[i]);
    }
    for (int i=1;i<=maxn*2;i++)
        c[i]+=c[i-1];
    for (int i=0;i<=maxn*2;i++)
        ans=max(ans,c[i]);
    cout<<ans;
    return 0;
}

```

  • 写回答

1条回答 默认 最新

  • 全栈若城 全栈领域优质创作者 2023-06-04 00:30
    关注

    前言
    编写不易给个采纳哦!

    分析
    根据题意,我们要从 $n$ 个物品中选取若干个物品,使得它们的总和为 $m$。对于第 $i$ 个物品,可以选择若干次。最终输出的结果就是所有可能方案中满足条件的方案数目之和。
    在本代码中,我们定义了一个二维数组 $f[i][j]$ ,表示前 $i$ 个物品中选取若干个物品,使得它们的总和为 $j$ 的方案数目。
    我们可以使用三重循环来填写这个数组。具体来说,外层循环枚举物品编号 $i$ ,中间循环枚举背包容量 $j$ ,内层循环枚举第 $i$ 个物品选取的次数 $k$ 。在每次循环中,我们根据状态转移方程 $f[i][j]=\sum_{k=0}^{\infty}f[i-1][j-k\times a[i]]$ 来更新 $f[i][j]$ 的值。其中, $a[i]$ 表示第 $i$ 个物品的价值。
    但是,这种方法的时间复杂度是 $O(nm^2)$ ,当 $n$ 和 $m$ 较大时,会导致程序运行超时。
    因此,可以对上述代码进行优化。一种优化方法是使用滚动数组的技巧,将二维数组 $f[i][j]$ 转化为一维数组 $f[j]$ ,这样就可以减少一个维度的循环。具体来说,我们可以将循环顺序改为:外层循环枚举物品编号 $i$ ,内层循环倒序枚举背包容量 $j$ (注意,倒序循环的原因是防止后面的状态覆盖前面的状态,从而出现重复统计)。在循环每个内层循环时,我们根据状态转移方程 $f[j]=\sum_{k=0}^{\infty}f[j-k\times a[i]]$ 来更新 $f[j]$ 的值。
    此外,对于循环内的第三重循环,也可以进行优化。具体来说,我们可以根据当前物品的价值 $a[i]$ 来计算出最多可以选取多少个该物品,然后只需循环这些次数即可,而不需要循环所有非负整数。

    效果截图

    img

    源码

    
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 5001;
    const int mod = 1000000000;
    int f[maxn],a[maxn];
    int main(){
        int n,m;
        cin >> n >> m;
        for(int i = 1;i <= n;i++){
            a[i] = i;
        }
        f[0] = 1; // 边界条件
        for(int i = 1;i <= n;i++){
            int limit = m / a[i]; // 计算最多可以选取多少个物品 i
            for(int k = 1;k <= limit;k++){ // 只需循环这些次数
                for(int j = m;j >= k * a[i];j--){ // 倒序循环
                    f[j] = (f[j] + f[j-k*a[i]]) % mod; // 状态转移方程
                }
            }
        }
        cout << f[m] << endl; // 输出结果
        return 0;
    }
    
    
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 7月12日
  • 已采纳回答 7月12日
  • 修改了问题 7月12日
  • 创建了问题 6月3日

悬赏问题

  • ¥15 python怎么在已有视频文件后添加新帧
  • ¥20 虚幻UE引擎如何让多个同一个蓝图的NPC执行一样的动画,
  • ¥15 fluent里模拟降膜反应的UDF编写
  • ¥15 MYSQL 多表拼接link
  • ¥15 关于某款2.13寸墨水屏的问题
  • ¥15 obsidian的中文层级自动编号
  • ¥15 同一个网口一个电脑连接有网,另一个电脑连接没网
  • ¥15 神经网络模型一直不能上GPU
  • ¥15 pyqt怎么把滑块和输入框相互绑定,求解决!
  • ¥20 wpf datagrid单元闪烁效果失灵