转载:http://blog.csdn.net/qian99/article/details/39138329

题意:给出n个物品,每个物品有两种属性Wi,Ti,有q组查询,每组查询要求在n个物品中选出一些,并使得两个属性的和为Mi,Si。

思路:刚开始看感觉是神题,后来仔细想了想,其实本质上就是个背包。最裸着写的话,那么就是dp[i][j][k]表示使用前i个物品,是否可以凑出第一个属性j,第二个属性k,要输出方案的话记录一下路径就可以了。一开始这么写了一发,加了一些乱七八糟的优化,还是会T。虽然这题时限还算宽,但这么写复杂度还是太高了。考虑到第一个属性最多只有50,那么可以用一个二进制数来表示是否能凑出第一个属性的情况,即:第i位为1表示可以凑出i。使用这种方法的好处是对于物品i可以直接算出第一种属性的组合情况,枚举一下新增的位,更新一下结果就行了。

代码:

#include<iostream>

#include<cstdio>

#include<cstring>

#include<string>

#include<algorithm>

#include<map>

#include<queue>

#include<stack>

#include<set>

#include<cmath>

#include<vector>

#define inf 0x3f3f3f3f

#define Inf 0x3FFFFFFFFFFFFFFFLL

#define eps 1e-8

#define pi acos(-1.0)

using namespace std;

typedef long long ll;

typedef unsigned long long ull;

const int maxn = 400 + 10;

const int M = 51;

short ans[200010][52];

ull f[200010];

int W[maxn],T[maxn];

map<ull,int>mp;

int main()

{

//    freopen("in.txt","r",stdin);

//    freopen("out.txt","w",stdout);

    for(int i = 1;i <= M + 1;++i)

        mp[1LL<<(i-1LL)] = i;

    int t,n,q;

    scanf("%d",&t);

    while(t--)

    {

        scanf("%d%d",&n,&q);

        memset(ans,0,sizeof(ans));

        memset(f,0,sizeof(f));

        f[0] = 1;

        ull v,x;

        for(int i = 1;i <= n;++i)

        {

            scanf("%d%d",&W[i],&T[i]);

            for(int j = 200000;j >= T[i];--j)

            {

                v = f[j];                        //f[j]表示第二个属性为j时,能够凑出的第一个属性的集合,用一个二进制数表示,第i位为1表示可以凑出这个数

                f[j] |= (f[j - T[i]]<<W[i]) & ((1LL<<M+1) - 1);          //计算使用当前物品能够得到的新的集合,在集合f[j - T[i]]添加W[i]的物品,

                                                                         //即原来能得到的每个值加上W[i],等价于将其左移W[i]位

                for(ull k = v ^ f[j];k ; k &= k-1)               //枚举新增加的集合

                {

                    x = (k ^ (k - 1)) & k;                      

                    ans[j][mp[x] - 1] = i;                       //将新增的位置更新,记录是使用了哪个物品达到的这个状态

                }

            }

        }

        int m,s,p;

        for(int i = 0;i < q;++i)

        {

            scanf("%d%d",&m,&s);

            if(!ans[s][m])

                puts("No solution!");

            else

            {

                printf("%d",ans[s][m]);

                p = ans[s][m];

                m -= W[p];

                s -= T[p];

                while(m)

                {

                    p = ans[s][m];

                    printf(" %d",p);

                    m -= W[p];

                    s -= T[p];

                }

                puts("");

            }

        }

    }

    return 0;

}

zoj 3812 状压dp的更多相关文章

  1. Problem Arrangement ZOJ - 3777(状压dp + 期望)

    ZOJ - 3777 就是一个入门状压dp期望 dp[i][j] 当前状态为i,分数为j时的情况数然后看代码 有注释 #include <iostream> #include <cs ...

  2. ZOJ 3306 状压dp

    转自:http://blog.csdn.net/a497406594/article/details/38442893 Kill the Monsters Time Limit: 7 Seconds ...

  3. ZOJ - 3777(状压dp)

    The 11th Zhejiang Provincial Collegiate Programming Contest is coming! As a problem setter, Edward i ...

  4. Most Powerful(ZOJ 3471状压dp)

    题意:n个原子,两两相撞其中一个消失,产生能量,给出任意两原子相撞能产生的能量,求能产生的最大能量. 分析:dp[i]表示情况为i时产生的最大能量 /*#include <map> #in ...

  5. Survival(ZOJ 2297状压dp)

    题意:有n个怪,已知杀死第i个怪耗费的血和杀死怪恢复的血,和杀死boss耗的血,血量不能超过100,若过程中血小于0,则失败,问 是否能杀死boss(boss最后出现). 分析:就是求杀死n个怪后剩余 ...

  6. Long Dominoes(ZOJ 2563状压dp)

    题意:n*m方格用1*3的方格填充(不能重叠)求有多少种填充方法 分析:先想状态,但想来想去就是觉得不能覆盖所有情况,隔了一天,看看题解,原来要用三进制 0 表示横着放或竖放的最后一行,1表示竖放的中 ...

  7. Travel(HDU 4284状压dp)

    题意:给n个城市m条路的网图,pp在城市1有一定的钱,想游览这n个城市(包括1),到达一个城市要一定的花费,可以在城市工作赚钱,但前提有工作证(得到有一定的花费),没工作证不能在该城市工作,但可以走, ...

  8. ZOJ 3723 (浙大月赛)状压DP

    A了一整天~~~终于搞掉了. 真是血都A出来了. 题目意思很清楚,肯定是状压DP. 我们可以联系一下POJ 1185  炮兵阵地,经典的状压DP. 两道题的区别就在于,这道题的攻击是可以被X挡住的,而 ...

  9. ZOJ 3777 - Problem Arrangement - [状压DP][第11届浙江省赛B题]

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3777 Time Limit: 2 Seconds      Me ...

随机推荐

  1. [Swift通天遁地]一、超级工具-(9)在地图视图MKMapView中添加支持交互动作的标注图标

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  2. Photoshop CC2019破解版

    Photoshop CC2019 精简版: 链接:https://pan.baidu.com/s/1PeFrhtLHxLRXCW_vMkAZDg  提取码:q6nl Photoshop CC2019: ...

  3. EditText(7)EditText输入事件监听

    EditText.addTextChangedListener(TextWatcher watcher); void initSearch(){ search = (EditText) findVie ...

  4. 论tab切换的几种实现方法

    tab切换在网页中很常见,故最近总结了4种实现方法. 首先,写出tab的框架,加上最简单的样式,代码如下: <!DOCTYPE html> <html> <head> ...

  5. SQL优化器简介

    文章导读: 什么是RBO? 什么是CBO? 我们在工作中经常会听到这样的声音:"SQL查询慢?你给数据库加个索引啊".虽然加索引并不一定能解决问题,但是这初步的体现了SQL优化的思 ...

  6. 6.12---esultMap查询所有

  7. iOS规范化时间格式,object-C计算指定时间与当前的时间差

    object-c计算指定时间与当前的时间差 头文件(.h): #import <Foundation/Foundation.h> @interface LuDate : NSDate +( ...

  8. 扩展Snackbar 使其支持居中显示

    https://github.com/nispok/snackbar 默认Snackbar支持底部或者顶部显示,不支持居中显示 查看Snackbar.java的源码可以看到createMarginLa ...

  9. IDEA提示 found duplicate code

    原因: IntelliJ IDEA提示Found duplicated code in this file 这不是我们代码错误,而是idea提示说我们的代码有重复,在项目的其他地方有同样的代码片段 解 ...

  10. vue+vux+es6+webpack移动端常用配置步骤

    1.创建项目(vue项目的流程就不多讲了)2.cnpm install vux --save3.在build/webpack.base.conf.js配置:const vuxLoader = requ ...