状态压缩DP--洛谷P1441砝码称重--java实现
作者:互联网
题目链接:
P1441 砝码称重 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目大意:
给定n个砝码,不同重量,可以重复。去掉其中m个后,能够称重多少种类的重量。
解析:
-
整体
我们枚举每一种砝码的排列方法,根据题目条件:去掉m个后,这个标准,过滤出能够使用的排列方法。
根据这些排列方法,计算出最终能够称多少种类的重量。
-
n个砝码找出符合去掉m个条件的排列方式
n个砝码的组合数,容易看出是
\[2^n -1 \]种.
所以可以枚举这些数字。
计算出它们二进制中1的个数cnt,如果n-cnt==m,说明符合条件。
-
关于如何计算二进制中1的个数
这里给出一种方法。
对于一个二进制数a,对它进行减一后,从右往左看第一个出现的1会变为0.
所以循环使用a=(a&(a-1))的方式,直到a为0.每次循环,计数自增。
最终计数的数量就是1的数量。
代码:
private static int getOneCnt(int num){ int cnt = 0; while (num!=0){ cnt++; num = num&(num-1); } return cnt; }
-
计算当前排列下,一共有多少种称重方式
这里采用二进制的方式表示能够称哪一种重量。
-
称重举例
按照题目测试用例:
三个砝码1,2,2,去除1个。
假设去除2。
剩下两个砝码1,2.
它们可以组成三种重量:1,2,3.
所以我们可以用三位二进制表示:
从右往左:
第一位1:代表能够称重1的重量
第二位1:代表能够称重2的重量
第三位1:代表能够称重3的重量
-
一般情况
假设一共能够称重的最大数量为n,那么使用n位二进制。某一位是1,代表从右往左数的这一位能够被测量出来。
所以对于最大数量位n,需要十进制数为
\[2^n -1 \] -
如何表示所有的重量
-
表示称重当前重量
我们使用一个数字a=1.表示它最低位是1,为了下面位移使用。
如果使用了第i个砝码weight[i],可以用a<<weight[i]来表示我们可以测量的重量。
因为1向左移动了weight[i],所以从右往左的weight[i]位置上是1,表示这个重量能够被测量。
(如果这段不太理解,可以再看一下上一步)
-
将重量累加
假设原本记录的重量是oldWeight,增加的重量是weight,增加后的重量是newWeight.
先给出计算方法:
newWeight = oldWeight|(oldWeight<<weight)
-
oldWeight<<weight说明
字面意思是oldWeight左移了weight位。
结合上面的分析,这个可以理解成,
对于oldWeight,针对每一种重量,都再增加了weight的重量,作为新的,使用了weight这个砝码后,能够称重的重量。
如果原本oldWeight[i]是0,表示i索引代表的重量不能够被称重,那么即使进行了左移,也不能被称重。
如果原本oldWeight[i]是1。增加了weight重量后,能够被称重的重量是oldWeight[i]向左移动weight的位置。
-
oldWeight|(oldWeight<<weight)说明
将(oldWeight<<weight)记为useWeight。表示使用了weight砝码后能够称重的重量。
那么如何将原本的重量和现在的useWeight加起来?
使用按位或运算。这样子得到的结果,就是不适用weight砝码时能够测量的所用重量+使用了weight后能够测量的所有重量。
或运算:
有1则1----如果某种重量能够被测量了,就用1表示。
全0则0---如果oldWeight不能测量这个重量,oldWeight<<weight也不能测量这个重量,那么或运算后,这个重量所代表的二进制位置仍旧位0,不能测量。
于是:
newWeight = oldWeight|(oldWeight<<weight)
-
至此,分析完毕。
-
一点问题:
题目给定的数据范围:
\[n≤20 \]\[m≤4 \]\[m < n \]\[a i ≤100 \]表示最多20个砝码,每个砝码最多100.所以最多称重100*20=2000.
按照上面分析,需要的排列数是
\[2^{100} -1 \]java中,long最大是
\[2^{64} -1 \]所以大小远远不够。
所以我们使用java API中的BitSet来进行二进制的计算。
主要使用到的方法:
-
bitset.get(i)
获取第i位的bit数。
-
bitset.set(i,j)
将第i位的bit置为j
-
bitset.or(anotherBitSet)
或运算,结果赋值给bitset
-
bitset.cardinality()
获取当前bitset中,多少位是1.
java代码:
import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.BitSet; import java.util.StringTokenizer; public class Main { public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); int n = Integer.parseInt(st.nextToken()); int m = Integer.parseInt(st.nextToken()); int[] weight = new int[n]; st = new StringTokenizer(br.readLine()); for(int i = 0;i<n;i++){ weight[i] = Integer.parseInt(st.nextToken()); } int oneCnt = 0; int result = 0; for(int i = 0;i<(1<<n);i++){ oneCnt = getOneCnt(i); if(n-oneCnt != m){ continue; } BitSet total = new BitSet(); total.set(0); BitSet temp; for(int j = 0;j<n;j++){ if((i&(1<<j))==0){ continue; } temp = (BitSet) total.clone(); temp = moveLeft(temp,weight[j]); total.or(temp); } result = Math.max(result,total.cardinality()); } //初始化total是1.因为后面需要进行移位操作 //这一位表示称重0的重量. //实际上这个重量不能称出来,所以result-1. System.out.println(result-1); } private static int getOneCnt(int num){ int cnt = 0; while (num!=0){ cnt++; num = num&(num-1); } return cnt; } private static BitSet moveLeft(BitSet bitset,int offset){ BitSet result = new BitSet(); for(int i = 0;i< bitset.length();i++){ result.set(i+offset,bitset.get(i)); } return result; } }
-
标签:洛谷,weight,P1441,--,oldWeight,砝码,java,重量,称重 来源: https://www.cnblogs.com/reclusiveone/p/14875252.html