51nod1883 升级卡牌

1883 升级卡牌

Noder在玩炉石,现在轮到Noder进行攻击了,在这一轮中,Noder可以选择8张牌对敌人进行攻击,一张牌使用一次就会消失。在攻击之前Noder可以对他的牌进行升级,然后就可以发挥更大的威力。但是升级是需要钱的。现在Noder手上有M块钱,N张牌。第i张牌的等级范围是1到K[i];第i张牌在第j个等级的时候攻击力是A[i][j]。每次升级的时候,只能给一张牌升一级,但是可以进行多次升级。将第i张牌从第j级升到第j+1级,需要C[i][j]块金钱。在未做任何升级操作的时候,第i张牌的等级是L[i]。

现在Noder想用这些钱的一部分对某些牌进行升级,最后使用8张牌去攻击对手,最后的目的是使得攻击力总和最大。

样例解释:

在样例1中,把前4张牌升级到等级3,把第5,第6张牌升级到等级2,最后两张牌不升级,那么最后的花费是(1+2)+(1+3)+(1+4)+(1+5)+1+1=20块,总攻击力是100+100+100+100+10+10+1+1=422

输入

多组测试数据。
第一行有一个整数T(1 ≤ T ≤ 10),表示测试数据的数目。
接下来有T组数据。
对于每一组数据,
第一行有两个整数M (1 ≤ M ≤ 1,000,000,000)和 N(8 ≤ N ≤ 12)
接下来有N个块。
每个块有三行,表示第i张牌的信息。
第一行有两个整数K[i] (1 ≤ K[i] ≤ 10)和 L[i]( 1 ≤ L[i] ≤ K[i])。
第二行有K[i]个整数A[i][1],A[i][2], ..., A[i][K[i]],表示每一个等级所对应的攻击力。
第三行有K[i]-1个整数C[i][1],C[i][2], ..., C[i][K[i]-1]。

输出

对于每一组数据,输出一个整数表示答案,占一行。

输入样例

样例输入1
2
20 8
3 1
1 10 100
1 2
3 1
1 10 100
1 3
3 1
1 10 100
1 4
3 1
1 10 100
1 5
3 1
1 10 100
1 6
3 1
1 10 100
1 7
3 1
1 10 100
1 8
3 1
1 10 100
1 9
30 10
4 1
1 10 100 200
1 2 3
3 1
1 10 100
2 4
3 1
1 10 100
3 6
4 2
1 10 100 200
4 8 16
3 1
1 10 100
5 10
3 1
1 10 100
6 12
3 1
1 10 100
7 14
3 1
1 10 100
8 16
3 1
1 10 100
9 18
3 1
1 10 100
10 20

输出样例

样例输出1
422
504

分析:

对于这个问题,那些不用牌很明显不会去升级,那么就要先确定哪些牌是用的,然后给这些牌升级,如果直接枚举哪些牌用,然后再枚举哪些牌升到什么等级,这样的复杂度是 C(12,8)∗108很明显会超时。

其实对于108这一步有优化,即把这8张牌分成两部分,然后分别先暴力求出所有组合,那么左边就是104种可能,右边也有104种可能,然后按照总的花费进行排序。最后枚举左边中的一个组合,去右边中找那些能够和他组合起来花费不超过M的,这儿可以用two point算法,因为花费已经有序了,那么随着左边的花费增大,右边的最大总花费是要减少的。

放代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 13;
const int M = 10100;
struct Node{
    unsigned int a;int c;
    Node(unsigned int _a=0,int _c=0){
        a=_a; c=_c;
    }
};
bool cmp(Node &a,Node &b){
    return a.c<b.c;
}
int n,m,l[N],r[N],s[N],a[N][N],c[N][N];
unsigned int na; ll ans; int nc;
Node pp[M],qq[M]; int p,q;
void dfs1(int d){
    if(nc>m) return ;
    if(d>4){
        pp[p].a=na; pp[p].c=nc;
        p++; return;
    }
    int j=s[d];
    for(int i=l[j];i<=r[j]&&~c[j][i];++i){
        na+=a[j][i]; nc+=c[j][i];
        dfs1(d+1);
        na-=a[j][i]; nc-=c[j][i];
    }
}
void dfs2(int d){
    if(nc>m) return ;
    if(d>8){
        qq[q].a=na; qq[q].c=nc;
        q++; return;
    }
    int j=s[d];
    for(int i=l[j];i<=r[j]&&~c[j][i];++i){
        na+=a[j][i]; nc+=c[j][i];
        if(nc>m){
            na-=a[j][i]; nc-=c[j][i];
            break;
        }
        dfs2(d+1);
        na-=a[j][i]; nc-=c[j][i];
    }
}
void dfs(int d,int k){
    if(n+k-d<8) return;
    if(k>8){
        p=q=0; na=nc=0;
        dfs1(1); dfs2(5);
        sort(pp,pp+p,cmp);
        sort(qq,qq+q,cmp);
        for(int i=p-1,j=0;i>=0;--i){
            while(j<q&&qq[j].c+pp[i].c<=m){
                if(na<qq[j].a) na=qq[j].a; j++;
            }
            if(ans<(ll)na+pp[i].a) ans=(ll)na+pp[i].a;
        }
        return;
    }
    s[k]=d; dfs(d+1,k+1);
    dfs(d+1,k);
}

int main(){
    int t; scanf("%d",&t);
    while(t--){
        scanf("%d %d",&m,&n); ans=0;
        for(int i=1;i<=n;++i){
            scanf("%d %d",&r[i],&l[i]);
            for(int j=1;j<=r[i];++j) scanf("%u",&a[i][j]);
            for(int j=2;j<=r[i];++j) scanf("%d",&c[i][j]);
            c[i][l[i]]=0;
            for(int j=l[i]+1;j<=r[i];++j){
                c[i][j]+=c[i][j-1];
                if(c[i][j]>m){
                    c[i][j]=-1; break;
                }
            }
        }
        dfs(1,1); printf("%lld\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值