前置知识:状压DP

洛谷传送门

emm....看到题目,我第一个想到的就是枚举。暴力大法好!

具体怎么枚举?当然是子集枚举啦!枚举出每一个可能的砝码选择方案。对于每一个合法的(也就是选取数量等于\(n-m\)的)方案,求出这个方案能称出重量的数量。至于如何求重量的数量,枚举出这个方案所有的子方案,再对每个子方案的和去重。

这个方法实在是太暴力了

Code:

#include <bits/stdc++.h>
using namespace std;
#define MAXN 22
#define MAXA 2005
int n,m,a[MAXN],ans,sum[1<<22],cnt[1<<22];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
}
for(int i=1;i<(1<<n);i++){//状压
int high_bit=0,high_bit_num=0;
for(int j=31;j>=0;j--){
if((i>>j)&1){
high_bit=1<<j;
high_bit_num=j;
break;
}
}//求出当前方案的最高位
sum[i]=sum[i^high_bit]+a[high_bit_num+1];//转移。因为i是“按顺序”枚举的,所以去掉最高位后的方案一定枚举过了。
cnt[i]=cnt[i^high_bit]+1;
}
for(int i=1;i<(1<<n);i++){
if(cnt[i]==n-m){
set<int> heavy;//set有自动去重的功效
for(int j=1;j<=i;j++){
if((j|i)==i){
heavy.insert(sum[j]);//枚举每个子方案
}
}
ans=max(ans,int(heavy.size()));//取大
}
}
printf("%d\n",ans);
return 0;
}

但是,虽然暴力打得很爽,时间复杂度也非常爆炸。时间复杂度达到了可怕的\(O((2^{n})^2)\)! 所以,优化是必须的。

可以考虑对求重量的数量的过程进行优化。定义状态\(dp(i)\)代表当前方案能否称出重量\(i\),\(a(j)\)代表当前考虑的砝码(当然,这个砝码必须包含在当前方案里),容易想出像下面这样的状态转移方程:

\[dp(i)=dp(i-a(1)) \operatorname{OR} dp(i-a(2))...\operatorname{OR} dp(i-a(n))
\]

这个方程翻译成人话的意思就是:如果一个重量可以被组合出来,那么再加一个砝码也能被组合出来。反过来就是,如果一个重量减去一个砝码的重量能被组合出来,那么这个重量能够被组合出来。

有了状态转移方程,代码就非常好写了。

#include <bits/stdc++.h>
using namespace std;
#define MAXN 22
#define MAXA 2005
int n,m,a[MAXN],cnt[1<<22],dp[MAXA],ans;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
}
for(int i=1;i<(1<<n);i++){//状压
int high_bit=0,high_bit_num=0;
for(int j=31;j>=0;j--){
if((i>>j)&1){
high_bit=1<<j;
high_bit_num=j;
break;
}
}//求出当前方案的最高位
cnt[i]=cnt[i^high_bit]+1;//转移。因为i是“按顺序”枚举的,所以去掉最高位后的方案一定枚举过了。
}
for(int i=1;i<(1<<n);i++){
if(cnt[i]==n-m){
fill(dp,dp+2000+1,false);
dp[0]=true;
for(int j=1;j<=n;j++){
if(i>>(j-1)&1){//如果当前砝码在方案里才考虑
for(int k=2000;k>=a[j];k--){
dp[k]|=dp[k-a[j]];
}
}
}
int cnt=0;
for(int k=1;k<=2000;k++){
if(dp[k]){
cnt++;
}
}
ans=max(ans,cnt);
}
}
printf("%d\n",ans);
return 0;
}

时间复杂度是\(O(2^n\times n \times \max{a_i})\)仍然非常高,但比之前的不知道低到哪里去了,足以通过本题。

你都看到这儿了不考虑点一个赞吗

[状压DP]P1441 题解 砝码称重的更多相关文章

  1. POJ - 3254 Corn Fields(状压DP)题解

    思路: 参照blog,用状压DP做,和题解稍微有点不一样,我这里直接储存了状态而不是索引. 这一题的问题是怎么判断相邻不能种,我们用2进制来表示每一行的种植情况.我们将每一行所能够造的所有可能都打表( ...

  2. FJNU Fang G and his Friends(状压DP)题解

    Description     众所周知,fang G 有很多小伙伴,有一天,Fang G 打算带他们去玩有趣的游戏OOXX,这个游戏需要分成两组,有趣的是,每个人互相之间都有一个满意度,大家都想和自 ...

  3. POJ 2923 Relocation(状压DP)题解

    题意:有2辆车运货,每次同时出发,n(<10),各自装货容量c1 c2,问最少运几次运完. 思路:n比较小,打表打出所有能运的组合方式,用背包求出是否能一次运走.然后状压DP运的顺序. 代码: ...

  4. HDU 4272 LianLianKan(状压DP)题解

    题意:一个栈,每次可以选择和栈顶一样的数字,并且和栈顶距离小于6,然后同时消去他们,问能不能把所有的数消去 思路:一个数字最远能消去和他相距9的数,因为中间4个可以被他上面的消去.因为还要判断栈顶有没 ...

  5. HDU 4628 Pieces(状压DP)题解

    题意:n个字母,每次可以删掉一组非连续回文,问你最少删几次 思路:把所有回文找出来,然后状压DP 代码: #include<set> #include<map> #includ ...

  6. HDU 2825 Wireless Password(AC自动机 + 状压DP)题解

    题意:m个密码串,问你长度为n的至少含有k个不同密码串的密码有几个 思路:状压一下,在build的时候处理fail的时候要用 | 把所有的后缀都加上. 代码: #include<cmath> ...

  7. 洛谷P1879 [USACO06NOV]玉米田Corn Fields【状压DP】题解+AC代码

    题目描述 Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ...

  8. Codeforces 744C Hongcow Buys a Deck of Cards 状压dp (看题解)

    Hongcow Buys a Deck of Cards 啊啊啊, 为什么我连这种垃圾dp都写不出来.. 不是应该10分钟就该秒掉的题吗.. 从dp想到暴力然后gg, 没有想到把省下的红色开成一维. ...

  9. POJ 1185 炮兵阵地(状压DP)题解

    思路:和上一篇思路一样,但是这里要求最大能排几个,这里要开三维,记录上次和上上次的状态,再一一判定,状态转移方程为 dp[i][j][k] = max(dp[i][j][k],dp[i - 1][k] ...

随机推荐

  1. Python File close() 方法

    概述 close() 方法用于关闭一个已打开的文件.高佣联盟 www.cgewang.com 关闭后的文件不能再进行读写操作, 否则会触发 ValueError 错误. close() 方法允许调用多 ...

  2. PHP addAttribute() 函数

    实例 给根元素和 body 元素添加一个属性: <?php$note=<<<XML<note>高佣联盟 www.cgewang.com<to>Tove& ...

  3. 7.6 NOI模拟赛 灯 根号分治

    比较容易想的题目~ 容易发现 点亮一种颜色的贡献=新增灯的数量-已经存在的边的条数. 用线段树维护并不容易.暴力的话复杂度是\(Q\cdot n\)的. 考虑根号分治 只单纯考虑度数<B的点的话 ...

  4. CF R 209 div 2 CF359B Permutation 构造

    LINK:Permutation 休闲一下 开了一道构造题. 看起来毫无头绪 其实仔细观察第二个条件 0<=2k<=n. 容易想到当n是奇数的时候 k的范围更小 再手玩一下第一个条件 容易 ...

  5. 学习使用CompletableFuture

    CompletableFuture 一.前言 1.JDK5的异步处理方式 2.JDK8的异步处理方式 二.学习CompletableFuture 1.结果获取方式 2.创建CompletableFut ...

  6. 账本APP开发

    服务端开发 好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 前言 我平时喜欢用账 ...

  7. 41-native关键字的理解

    使用 native 关键字说明这个方法是原生函数,也就是这个方法是用 C/C++等非Java 语言实现的,并且被编译成了 DLL,由 java 去调用. (1)为什么要用 native 方法 java ...

  8. demo2动态加载显示商品详情页

    /* 要求:实现 头像+昵称(多余7位用...) 商品图片(根据商品实际的图片的大小进行动态的展示.按照一定的比例进行展示.) 产品简介.产品简介在商品图片的下边.并跟随商品图片的大小进行动态的收缩或 ...

  9. C++虚函数相关内容

    样例代码 class Base{public: Base(){}; virtual ~Base(){    //若没有设置为虚函数:如果有这样的指针Base *p=new Derived();声明,则 ...

  10. Android Spinner的简单用法。

    今天学到的是spinner,就是下拉列表,这可不是ExpandListView哈. 闲话不解释.这是控件,所以先上布局:就不上线性布局了,基本上可以总结出,控件都得在布局里写,写之前嵌个布局就行. & ...