宁波工程学院2020新生校赛 E 皮卡丘这么可爱,当然要.....(多重背包二进制优化裸题)

15 篇文章 2 订阅

题目链接

题目描述
训练师小梁在一次机缘巧合中,发现了一个皮卡丘部落,她非常喜欢皮卡丘,但由于精灵球有限,所以她打算在这里逗留一段时间,部落中有\text{n}n个皮卡丘,每个皮卡丘有不同的可爱度qi ,小梁要欣赏这些皮卡丘,但有的皮卡丘被看多了会抑郁,所以她要合理的分配时间和看的次数,收获最多的可爱度。
输入描述:
到达部落的时间e, s(24小时制时间),皮卡丘的个数n(s≤e,n≤10 5 )下面n行t,q,s分别代表:欣赏这只皮卡丘需要的时间(分钟),这只皮卡丘的可爱度,这只皮卡丘最多能看几次(s=0表示这只皮卡丘脾气很好,能看无限次)
输出描述:
一个整数,表示能获取的最大可爱度
示例1
输入

5:30 7:10 5
3 1 5
4 4 2
2 1 0
4 5 3
5 6 0

输出

120

题目中把n个皮卡丘理解为n种物品,需要的时间理解为体积,获得的可爱度理解为价值,在加上有限定次数,可以显而易见的看出这道题是一道多重背包,再看题面中的数据范围n为10000,n^3的多重背包写法必定会超时,这就要用到了二进制优化。

二进制优化基于数论的一个知识,任意的一个整数中的任意的一个数,都可以由所有不大于这个整数的的

2^n的数组合而成,例如18=1+2+4+8+3, 18中的任意数都可以由1,2,4,8,3组合而成。

当p[i]=0时说明可以无限拿,可以直接转换成完全背包。

其中关于开始时间和结束时间转换成时间段大小有多种方法(这里列出两种,模拟或scanf的格式读入)

我们定义状态dp[s]表示当时间段大小为s时,能获得的最大可爱度。

状态转移方程为

dp[j]=max(dp[j],dp[j−t[i]]+c[i])
dp[k]=max(dp[k],dp[k−j∗t[i]]+j∗c[i])


我写的2进制优化程序只能过80%,原因不知道。

#include<bits/stdc++.h>
#include<bitset>
#include<unordered_map>
#define pb push_back
#define bp __builtin_popcount
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=1e6+100;
const int MOD=1e9+7;
int lowbit(int x){return x&-x;}
inline ll dpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % MOD; b >>= 1; t = (t*t) % MOD; }return r; }
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t); b >>= 1; t = (t*t); }return r; }
int n,m,x1,x2,y11,y22,t,q,s;
ll dp[maxn];
struct node{
    int x,y;
};
vector<node>G;
int main()
{
 
     scanf("%d:%d%d:%d%d",&x1,&y11,&x2,&y22,&n);
     m=(x2-x1)*60+y22-y11;
     for(int i=1;i<=n;i++)
     {
       scanf("%d%d%d",&t,&q,&s);//一次消耗的时间 一次获得的可爱度  次数
        if(s==0||s>m/t)s=m/t;
        for(int c=1;c<=s;c<<1)
        {
          G.push_back(node{t*c,q*c});
          s-=c;
        }
        if(s)G.push_back(node{t*s,q*s});
  
     }
  
     for(auto it:G)
     for(int j=m;j>=it.x;j--)
     {
        dp[j]=max(dp[j],dp[j-it.x]+it.y);
     }
     cout<<dp[m]<<endl;
  
     system("pause");
     return 0;
  
  
}

可能是因为vector储存方式比较慢导致的。

AC代码如下

#include<cstdio>
int n,m,s,f[10000],a[1100005],b[1100005],x1,y1,x2,y2,w,v,k,x;
inline int max(int a,int b){return a>b?a:b;}
int main()
{
    scanf("%d:%d%d:%d%d",&x1,&y1,&x2,&y2,&n);
    s=(x2-x1)*60+y2-y1;
    for(int i=0;i<n;++i)
      {
        scanf("%d%d%d",&w,&v,&k);
        if(k==0||k>s/w)k=s/w;
        for(x=1;x<=k;x<<=1)a[++m]=x*w,b[m]=x*v,k-=x;
        if(k)a[++m]=k*w,b[m]=k*v;
         
      }
    for(int i=1;i<=m;++i)
      {
        for(int j=s;j>=a[i];--j)
          f[j]=max(f[j-a[i]]+b[i],f[j]);
      }
    printf("%d",f[s]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值