• 【bzoj2073】【[POI2004]PRZ】位运算枚举子集的特技


    这里写图片描述
    (上不了p站我要死了)

    Description
    一只队伍在爬山时碰到了雪崩,他们在逃跑时遇到了一座桥,他们要尽快的过桥. 桥已经很旧了, 所以它不能承受太重的东西. 任何时候队伍在桥上的人都不能超过一定的限制. 所以这只队伍过桥时只能分批过,当一组全部过去时,下一组才能接着过. 队伍里每个人过桥都需要特定的时间,当一批队员过桥时时间应该算走得最慢的那一个,每个人也有特定的重量,我们想知道如何分批过桥能使总时间最少.
    Input
    第一行两个数: w – 桥能承受的最大重量(100 <= w <= 400) 和 n – 队员总数(1 <= n <= 16). 接下来n 行每行两个数分别表示: t – 该队员过桥所需时间(1 <= t <= 50) 和 w – 该队员的重量(10 <= w <= 100).
    Output
    输出一个数表示最少的过桥时间.
    Sample Input
    100 3
    24 60
    10 40
    18 50
    Sample Output
    42

    当然是状压dp。但是如何快速的枚举子集是一件头疼的事,就讲一下毫不冗杂的枚举子集好了:
    首先是代码:

    for(int i=1;i<(1<<n);i++)
        for(int j=i;j;j=(i&(j-1)))

    如此一来j就遍历了i的所有子集。可以模拟一下:每次减1,就将目前的最低位的1去掉了,而之前去掉的更小的1又出现了

    这个技巧很有用啊,要学会

    完整代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long 
    using namespace std;
    
    int sum,n,tt[20],ww[20];
    int f[1<<16],t[1<<16],w[1<<16];
    
    int main(){
        cin>>sum>>n;
        for(int i=1;i<=n;i++) cin>>tt[i]>>ww[i];
        for(int i=1;i<(1<<n);i++)
            for(int j=i,k=1;j;j>>=1,k++) if(j&1) t[i]=max(t[i],tt[k]),w[i]+=ww[k];
        memset(f,0x3f3f3f3f,sizeof(f));
        f[0]=0;
        for(int i=1;i<(1<<n);i++){
            for(int j=i;j;j=(i&(j-1))){
                if(w[j]<=sum) f[i]=min(f[i],f[i^j]+t[j]);
            }
        }
        cout<<f[(1<<n)-1]<<endl;
        return 0;
    }
  • 相关阅读:
    一款HTML5网页网络检测工具--LibreSpeed
    远程连接Linux中的Mysql数据库
    Ubuntu安装python-rrdtool模块
    Ubuntu 18.04 lxd和lxd-client导致版本过低无法apt安装
    7.JVM调优-方法区,堆,栈调优详解
    6.堆和GC
    5.java内存模型详细解析
    4.自定义类加载器实现及在tomcat中的应用
    3.代码实现自定义类加载器
    2.双亲委派机制详细解析及原理
  • 原文地址:https://www.cnblogs.com/LinnBlanc/p/7763108.html
Copyright © 2020-2023  润新知