【upc】Transport Ship | 二进制优化多重背包

题目描述

There are N different kinds of transport ships on the port. The i^th kind of ship can carry the weight of V[i] and the number of the ith kind of ship is 2C[i]-1. How many different schemes there are if you want to use these ships to transport cargo with a total weight of S? It is required that each ship must be full-filled. Two schemes are considered to be the same if they use the same kinds of ships and the same number for each kind.

 

输入

The first line contains an integer T(1≤T≤20), which is the number of test cases. 
For each test case: 
The first line contains two integers: N(1≤N≤20), Q(1≤Q≤10000), representing the number of kinds of ships and the number of queries.
For the next N lines, each line contains two integers: V[i] (1≤V[i]≤20), C[i] (1≤C[i]≤20), representing the weight the i^th kind of ship can carry, and the number of the i^th kind of ship is 2C[i]-1.
For the next Q lines, each line contains a single integer: S (1≤S≤10000), representing the queried weight.

 

 

输出

For each query, output one line containing a single integer which represents the number of schemes for arranging ships. Since the answer may be very large, output the answer modulo 1000000007.

 

样例输入

复制样例数据

1
1 2
2 1
1
2

样例输出

0
1

题目大意:有n个物品,每个物品的重量是v[i],数量有2^c[i]-1个,询问q次,每次询问有多少种组合能组成 数x

题目思路:

首先都可以想到朴素的思路,每一种物品能组成的种类数目是可以确定的,确定之后直接跑,但是那样跑背包dp会重复,例如 有一类物品,价值为2,有3个 ,所以 他能组成 2 4 6,跑2,2是一种,跑4,4是一种,6是一种,跑6,6自己也是一种,所以会造成重复.所以我们优化一下,从上个例子来也不难看出.2 4 是可以构成6的,为什么呢,这里有一个性质,把一个数拆分,小于该数的二进制数一定能够成该范围内的所有数,举个例子,假设 n=15:

那么 把n分成  1 2 4 8 ,3=1+2,4=1+3,6=2+4,7=1+2+4.

这样我们遍历 分成的二进制数就可以把所有的种类都遍历一遍且没有重复.注意dp的时候,我们从最大值到最小值反向DP,这样比较简单,否则正着dp需要判断很多条件.注意初始状态为 dp[0]=1即可.

AC代码:

#include <bits/stdc++.h>
#define E 2.718
using namespace std;
typedef  long long ll;
const ll INF=0x7f7f7f7f;
const int maxn=1e6+8;
const double eps=1e-10;
const ll mod=1000000007;
ll n,m,f;
ll v[50],c[50];
ll dp[10005];
ll save[500];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        memset(dp,0,sizeof(dp));
        ll cnt=0;
        scanf("%lld%lld",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%lld%lld",&v[i],&c[i]);
        for(int i=1;i<=n;i++)
            for(int k=0;k<c[i];k++)
                save[++cnt]=v[i]<<k;
        dp[0]=1;
        for(int i=1;i<=cnt;i++)
        {
            for(int k=10001;k>=save[i];k--)
            {
                dp[k]+=dp[k-save[i]];
                dp[k]%=mod;
            }
        }
        while(m--)
        {
            ll x;scanf("%lld",&x);
            printf("%lld\n",dp[x]);
        }
    }
    return 0;
}

总结:其实这种二进制优化,之前训练中讲过的,竟然忘了......,所以对于每一个知识点都需要练习.

1.以后遇到类似这种多重背包的问题,如果拆分数据很大,就采用二进制优化.

2.反向dp的使用,(可以优化代码量)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只酷酷光儿( CoolGuang)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值