zoj 3812 We Need Medicine

题目链接

题意:有n~400中化学成分,每种成分要么选取重量Wi~50,获得Ti的TEV~200000值,要么不取,获得0的TEV值。之后又Q~400种病毒询问,对于每种病毒,要求配置质量为Mi~50的药物,并且TEV值为Si~200000,求出针对此病毒的化学成分组成,任意一组就行。

题解:每次询问固定M和TEV值,看是否能够组成,本来M和TEV要合并作为dp的维数,但是这样浪费了dp的值,发现M只有50,longlong状压后可以用一个数表示,那么只用dp【TEV】就行了,打印成分用一个from【TEV】【no】就行。

重点:400个物品,01背包。但是每次查询问(TEV,w)是否存在,利用w~50状压作为dp的值。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)
#define lowbit(x) ((x)&(-x))//能够取出数字二进制下的最后一个1表示成10进制。

typedef long long ll;

using namespace std;

const int maxn = 2e5 + 100;
const int key = (1<<51) - 1;
int from[maxn][60], n, q, t[maxn], w[maxn];
int tot = 200000;
ll dp[maxn];
map<ll, int> real;

void getReal()//为了便于从ll转成编号。并且小心,只要跟状态有关的都用ll
{
    real.clear();
    for(long long i = 0;i <= 50;i++)
    {
        ll tmp = (1ll<<i);
        real[tmp] = i;
    }
}

void getDp()
{
    CLR(dp);
    CLR(from);//初始化为0
    dp[0] = 1;//0的状态w是0
    REP_D(i, 1, n)
    {
        for(int j = tot;j >= t[i];j--)
        {
            ll pre = dp[j];
            ll now = (dp[j - t[i]]<<w[i]);
            dp[j] = (pre|now);//搞出新的可以达到的情况
            for(ll k = ((now^pre)&now);k != 0;k -= lowbit(k))//这个小心点写,要保证很小,取出新添的,然后lowbit一点一点。
            {
                ll tt = lowbit(k);
                ll tmp = real[lowbit(k)];//记录
                from[j][tmp] = i;
            }
        }
    }
}
void solve()
{
    getReal();
    getDp();
    REP_D(i, 1, q)
    {
        int m, s;
        scanf("%d%d", &m, &s);
        ll sta = dp[s];
        if((sta&(1ll<<m)))//注意ll,输出。
        {
            int now = from[s][m];
            vector<int> ans;
            while(now != 0)
            {
                ans.push_back(now);
                s -= t[now];
                m -= w[now];
                now = from[s][m];
            }
            REP(j, 0, ans.size())
            {
                printf("%d%c", ans[j], (j == ans.size() - 1 ? '\n' : ' '));
            }
        }
        else
        {
            printf("No solution!\n");
        }
    }
}

int main()
{
   // freopen("2Bin.txt", "r", stdin);
    //freopen("3Bout.txt", "w", stdout);
    int ncase;
    scanf("%d", &ncase);
    while(ncase--)
    {
        scanf("%d%d", &n, &q);
        REP_D(i, 1, n)
        {
            scanf("%d%d", &w[i], &t[i]);
        }
        solve();
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值