NKOJ1633 神仙开山【变进制数状压DP】

不知道老板从哪里找来的野题,居然网上啥东西没有,写个题解吧

题面:
        ------------------------------------------------------

神仙姐姐来到一个美丽的地方,不过美中不足的是那儿有好些秃山……
由于神仙过于爱美,由不得有半点瑕疵,所以她想用仙力把这些儿秃山通通消灭掉!由于消灭一座山可能要消耗掉一些Hp Or Mp Or Rp……但是有的时候不必考虑那么多,你有可能只需要考虑HP和MP,甚至仅仅考虑HP.

输入格式

第一行有两个整数n (1<=n<=100)和m (1<=m<=5),分别表示山的数量和需要考虑的身体数值种类.
第二行m个整数 V1~Vm,表示神仙各种数值的初始值(各种数值消耗后不会回复);
第三行到第n+2行,分别是这n座山的资料,每行m+1个数。

 每行第一个数是消灭掉这座山可以得到的美观度
 第二到第m+1个数分别表示消灭掉这座山消耗的各种数值(对应第二行的数值)

输出格式

输出神仙姐姐可以得到的最大美观度(每种数值最后不可消耗到<0);
样例输入

5 1
6
7 3
8 5
3 1
6 2
4 3

样例输出

16

        ------------------------------------------------------

很明显,这道题是一道背包冻柜题,但是这道题的背包数量是1-5变化的,你也可以写五个数组拿来用,不过有更优秀的状压方法。

变进制数状态压缩:

看看我们熟悉的进制:
十进制数: x=x0×100+x1×101+x2×102+x3×103+
二进制数: x=x0×20+x1×21+x2×22+x3×23+

这些常用的进制都可以表示为一个多项式,每一位数值的范围就是它的进制,那以此类推:
m进制数: x=x0m0+x1m1+x2m2+x3m3+
每一位的数值x_0,x_1,x_2,x_3,…范围都是[0, m1 ]。

再以此类推,如果每一位进制不同?:
i 位范围是[0,mi1]:
变进制数: x=x0+x1m0+x2m0m1+x3m0m1m2+...
不妨设 Mi=i1j=0mj
x=x0+x1M1+x2M2+x3M3
利用上式,我们就可以把一个每一位范围是 [0,mi1] 的数列压缩成一个十进制数了
而从压缩状态还原某一位数,我们从十进制数120394中取出第3位,过程为:120394%1000/100=3,以此类推:
对于变进制数 xi=x%Mi+1Mi

然后这道题就是模板题了。。只需要把每种消耗状压一下就好,注意每次都需要判定每一位是否够用。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=105,M=1234567,Inf=1e9;
inline int _R(){
    int d=0;bool b=1;char t=getchar();
    while(t<'0'||t>'9'){if(t=='-')b=0;t=getchar();}
    for(;t>='0'&&t<='9';t=getchar())d=(d<<3)+(d<<1)+t-'0';
    return b?d:-d;
}
int n,m,t,Ans,P[N],W[N],v[N],c[N][N],w[N],f[M];
int main(){
    n=_R(),m=_R();
    W[1]=1;
    for(int i=1;i<=m;i++)P[i]=_R();
    for(int i=2;i<=m+1;i++)W[i]=W[i-1]*(P[i-1]+1);
    t=W[m+1]-1;
    for(int i=1;i<=n;i++){
        w[i]=_R();
        for(int j=1;j<=m;j++){
            c[i][j]=_R(),
            v[i]+=c[i][j]*W[j];
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=t;j>=v[i];j--){
            int k;
            for(k=1;k<=m;k++)if((j%W[k+1])/W[k]<c[i][k])break;
            if(k>m)f[j]=max(f[j],f[j-v[i]]+w[i]);
            }
    cout<<f[t];
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值