zoukankan      html  css  js  c++  java
  • 回归本心QwQ背包问题luogu1776

    今天在这里说一下多重背包问题 对

    之前一直没有怎么彻底理解

    首先多重背包是什么?这里就不做过多的赘述了

    朴素的多重背包的复杂度是(O(n*m*sum s[i])),其中(s[i])是每一件物品的数量

    for (int i=1;i<=n;i++)
        for (int k=1;k<=s[i];k++)
          for (int j=m;j>=k*c[i];j--)
             dp[j]=max(dp[j],dp[j-k*c[i]]+k*w[i]);
    

    但大多数题目,这种复杂度是不能允许的

    那么我们考虑优化

    首先我们考虑,怎么样快速表示(1~n)中所有的数呢?

    二进制!

    打个比方(n=6),那么我们就只需要1 2 3就能构成所有的数

    1=1
    2=2
    3=3
    4=1+3
    5=2+3
    6=1+2+3

    对于一个数n,我们只需要从小开始不停用n减去2的幂次方,若n大于0,则当前的二的幂次方合法,最后我们将合法的二的幂次方和最后的余数分别看成一个新的物品去做背包,就能表示出所有的(1~n)的数(可以理解为,小于n的数的二进制,一定不会比n大,那么构成n的这些二进制位,一定是够用的)

    那么我们只需要对(s[i])进行二进制拆分,然后把他们看成一个物品

    直接上代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    
    using namespace std;
    
    inline int read()
    {
      int x=0,f=1;char ch=getchar();
      while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
      while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
      return x*f;
    }
    
    const int maxn = 2e5+1e2;
    
    int f[maxn];
    int n,m;
    int c[maxn],w[maxn];
    int tmp ;
    int main()
    {
      n=read(),m=read();
      for (int i=1;i<=n;i++)
      {
        int y=read(),z=read(),x=read();
        int inv = 1;
        while (x)
        {
        	inv=min(inv,x);
        	w[++tmp]=inv*y;
        	c[tmp]=inv*z;
        	x-=inv;
        	inv<<=1;
        }
      }
      //for (int i=1;i<=tmp;i++) co
      memset(f,0xdf,sizeof(f));
      f[0]=0;
      for (int i=1;i<=tmp;i++)
      {
      	 for (int j=m;j>=c[i];j--)
      	 {
      	 	f[j]=max(f[j],f[j-c[i]]+w[i]);
           }
      }
      int ans=-1e9;
      for (int i=1;i<=m;i++) ans=max(ans,f[i]);
      cout<<ans;
      return 0;
    }
    

    QwQ 二进制优化的复杂度是(O(nlogn imes m))

    然而,我们可以用单调队列做到(O(nm))的(虽然据说常数很大)

    这里比较懒,直接放一个dalao的博客了
    这里写图片描述

    我就写几点自己的理解吧:

    首先,我们是枚举体积的余数和倍数,然后转移
    维护单调队列的时候,一定要注意维护队首元素过期
    虽然我也不知到这种算法的正确性,不过它运用的思路就是把同样的余数(m'),放到一起考虑对

    直接上代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    
    using namespace std;
    
    inline int read()
    {
       int x=0,f=1;char ch=getchar();
       while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
       while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
       return x*f;
    }
    
    const int maxn = 2e5+1e2;
    
    int q[maxn],t[maxn];
    int head=1,tail=0;
    
    void push(int x,int pos,int p)
    {
    	while (head<=tail && x>=q[tail])
    	  q[tail]=0,t[tail--]=0;
    	q[++tail]=x;
    	t[tail]=pos; 
    	while (head<tail && t[head]<pos-p) head++;
    }
    
    int c[maxn],w[maxn],s[maxn];
    int n,m;
    int f[110][maxn];
    
    
    int main()
    {
      n=read(),m=read();
      for (int i=1;i<=n;i++) w[i]=read(),c[i]=read(),s[i]=read();
      for (int i=1;i<=n;i++)
      {
      	//if (c[i]==0) while (1); 
      	int kk = m/c[i]; //
      	int num = min(s[i],kk); //最多能放多少个 
      	for (int j=0;j<c[i];j++) //枚举余数 
      	{
      		head=1,tail=0;
      		int ymh = (m-j)/c[i];
      		for (int k=0;k<=ymh;k++)
      		{
      			push(f[i-1][j+k*c[i]]-k*w[i],k,num);
      			f[i][j+k*c[i]]=max(f[i][j+k*c[i]],q[head]+k*w[i]);
      		}
      	}
      }
      int ans=-1e9;
      //for (int i=0;i<=m;i++) ans=max(ans,f[n][i]);
      for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++) ans=max(ans,f[i][j]);
      cout<<ans;
      return 0;
    }
    
    
  • 相关阅读:
    [置顶] 【玩转cocos2d-x之二十】从CCObject看cocos2d-x的内存管理机制
    android 随手记 读写文件的几种方式
    (队列的应用5.3.2)POJ 2259 Team Queue(队列数组的使用)
    iPhone调用ffmpeg2.0.2解码h264视频的示例代码
    android 随手记 仿微信的popwindow
    [LeetCode] Remove Nth Node From End of List
    [置顶] Zend Optimizer 和 Zend Debugger 同时安装
    uva 10721
    android实现六边形等不规则布局
    WPF中的TextBox隐藏边框
  • 原文地址:https://www.cnblogs.com/yimmortal/p/10160850.html
Copyright © 2011-2022 走看看