[HNOI2007]梦幻岛宝珠

题解:

一道比较好的题目

首先比较显然的就是我们要按照a*2^b的b的顺序来枚举

那么状态f[i][j]表示当前在b,用了a*2^b

刚开始没想到怎么不同层之间搞

看了题解发现非常简单

由于每一层到最后一层有用的二进制位至少时从自己的二进制位开始

所以我们可以舍弃那些没用的二进制位

maxa(f[i][j],f[i][k]+f[i-1][(j-k)*2])

常数优化就是那个(j-k)*2可以再min个1000

另外f[i][j]其实是个前缀和优化,所以刚开始都为0就可以了

不太想写对拍然后统计的sum数组忘记开ll了。。。。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define rint register int
#define IL inline
#define rep(i,h,t) for (rint i=h;i<=t;i++)
#define dep(i,t,h) for (rint i=t;i>=h;i--)
const int N=200;
int a[N],b[N],n,m,f[32][10010];
struct re{
  int a,b;
};
vector<re> ve[32];
IL int max(rint x,rint y)
{
  return x>y?x:y;
}
int main()
{
  ios::sync_with_stdio(false);
  while (cin>>n&&n!=-1)
  {
    rep(i,0,30) ve[i].clear();
    cin>>m;
    rep(i,1,n)
    { 
      cin>>a[i]>>b[i];
      int cnt=0;
      while (a[i]%2==0) a[i]/=2,cnt++;
      ve[cnt].push_back((re){a[i],b[i]});   
    }
    int ans=0;
    ll sum=0;
    rep(i,0,30)
    {
      rep(j,0,1000) f[i][j]=0;
      int l=int(ve[i].size())-1;
      rep(j,0,l) sum+=ve[i][j].a*(1<<i);
      rep(k,0,l)
        dep(j,1000,ve[i][k].a)
        {
          f[i][j]=max(f[i][j],f[i][j-ve[i][k].a]+ve[i][k].b);
        }
      if (i)
      {
        int kk=sum/(1<<i)+1;
     // int kk=1000;
        dep(j,kk,0)
          dep(k,j,0)
            f[i][j]=max(f[i][j],f[i][k]+f[i-1][min(1000,(j-k)*2)]);
        rep(j,kk+1,1000) f[i][j]=f[i][j-1];
    //    rep(j,0,1000) cout<<f[i][j]<<" ";
    //    cout<<endl;    
      }
      if ((m>>i)&1)
        rep(j,1,1000) f[i][j-1]=f[i][j];  
      ans=max(ans,f[i][0]);
    }
    cout<<ans<<endl;
  }
  return 0;
}

 

posted @ 2018-09-01 14:14  尹吴潇  阅读(576)  评论(0编辑  收藏  举报