zoukankan      html  css  js  c++  java
  • P5911 [POI2004]PRZ (状态压缩dp+枚举子集)

    题目描述

    luogu

    桥已经很旧了, 所以它不能承受太重的东西。任何时候队伍在桥上的人都不能超过一定的限制。 所以这只队伍过桥时只能分批过,当一组全部过去时,下一组才能接着过。队伍里每个人过桥都需要特定的时间,

    当一批队员过桥时时间应该算走得最慢的那一个,每个人也有特定的重量,我们想知道如何分批过桥能使总时间最少。

    数据范围: 对于 100% 的数据,\(100\leq W \leq400,1\leq n \leq16,1 \leq t\leq 50,10\leq w\leq 100\)

    前置芝士

    1. 位运算

    2. 枚举子集

    首先,我们先看一下枚举子集是什么东西。

    在状态压缩dp时,我们一般的套路就是枚举两个状态\(i\)\(j\),判断 \(j\) 是否是 \(i\) 的子集,这样来说复杂度时O(4^n)

    但,根据二项式定理,一个集合的子集最多有3^n 严格枚举的话,可以将复杂度变为O(3^n)

    代码

    对于这道题,\(n\) 的范围很小,我们可以考虑对n进行状态压缩

    \(f[i]\) 表示达到 \(i\) 这个状态所需要的最小时间 \(i\)时一个n位的二进制数。

    转移的话,我们可以枚举\(i\)的子集,就是考虑这次有哪些人乘船

    f[i] = min(f[i],f[i-j] + tim[j]);//i是我们想达到的状态,j是这次要运的状态,i-j是没运j这次之前的状态
    
    

    对于,每个状态所花费的时间,我们可以在之前就预处理出来。

    当然,你也可以在枚举 \(j\) 的时候算,只是这样你多算了很多状态,你就会稳稳的TLE

    看不懂的童鞋,下面代码有注释。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,m,t[20],w[20],base[20],f[65540],tim[65540],maxw[65540];
    inline int read()
    {
    	int s = 0, w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
    	return s * w; 
    }
    int main()
    {
    	m = read(); n = read();
    	for(int i = 0; i <= n-1; i++)
    	{
    		t[i] = read(); w[i] = read();
    	}
    	base[0] = 1;
    	for(int i = 1; i <= n; i++) base[i] = base[i-1] * 2;//处理一下2的进制
    	for(int i = 0; i < base[n]; i++)//枚举每个状态
    	{
    		for(int j = 0; j < n; j++)//枚举每个人
    		{
    			if((i & (1<<j)) == 0)//判断这个人在i这个状态是否已经乘船,没乘船的话,可以转移得到下一个状态
    			{
    				tim[i | (1<<j)] = max(tim[i],t[j]);//i|(1<<j)即把i的第j位赋1,就像于第j个人坐了船后,i所变成的状态
    				maxw[i |(1<<j)] = maxw[i] + w[j]; //进行转移
    			}
    		}
    	}
    	for(int i = 0; i < base[n]; i++) f[i] = 2333333;//初始化为无穷大
    	f[0] = 0;
    	for(int i = 1; i < base[n]; i++)//枚举每个状态
    	{
    		for(int j = i; j; j = (j-1) & i)//枚举子集
    		{
    			if(maxw[j] <= m) f[i] = min(f[i],f[i-j] + tim[j]);//j你可以理解为这次要运的状态,i-j就是i没运j之前i的状态
    		}
    	}
    	printf("%d\n",f[base[n]-1]);
    	return 0;
    }
    
    
  • 相关阅读:
    安装 Java 开发工具包JDK(Windows版本)
    在sublime text 3中让.vue文件的内容变成彩色
    iOS之禁止所有输入法的表情
    iOS之UIButton扩大按钮的响应区域
    iOS之利用腾讯Bugly程序调试,测试代码bug、卡顿等情况
    iOS之在本地搭建IPv6环境测试你的app
    iOS之让UISearchBar搜索图标和placeholder靠左显示
    iOS之限制TextField的输入长度
    iOS之oc与html之间的交互(oc中调用js的方法)
    iOS之面试题:腾讯三次面试以及参考思路
  • 原文地址:https://www.cnblogs.com/genshy/p/13437482.html
Copyright © 2011-2022 走看看