【状态压缩 meet in middle】poj3139Balancing the Scale
数组溢出真是可怕的事情
Description
You are given a strange scale (see the figure below), and you are wondering how to balance this scale. After several attempts, you have discovered the way to balance it — you need to put different numbers on different squares while satisfying the following two equations:
| x1 * 4 | + | x2 * 3 | + | x3 * 2 | + | x4 | = | x5 | + | x6 * 2 | + | x7 * 3 | + | x8 * 4 |
| y1 * 4 | + | y2 * 3 | + | y3 * 2 | + | y4 | = | y5 | + | y6 * 2 | + | y7 * 3 | + | y8 * 4 |
How many ways can you balance this strange scale with the given numbers?

Input
There are multiple test cases in the input file. Each test case consists of 16 distinct numbers in the range [1, 1024] on one separate line. You are allowed to use each number only once.
A line with one single integer 0 indicates the end of input and should not be processed by your program.
Output
For each test case, if it is possible to balance the scale in question, output one number, the number of different ways to balance this scale, in the format as indicated in the sample output. Rotations and reversals of the same arrangement should be counted only once.
Sample Input
87 33 98 83 67 97 44 72 91 78 46 49 64 59 85 88
0
Sample Output
Case 1: 15227223
题目大意
有16个重量互不相同的砝码,问有多少种不同的方案能够使这16个砝码分为两组分别平衡。两个方案不相同当且仅当它们不能够互相旋转或翻转得到。
题目分析
显然是道meet in middle,但是把什么状态折半呢?
$C_{16}^{8},C_{8}^{4}$
分两次折半搜索,先从16个里取8个,再从选出的8个里取4个。
先用二进制状态压缩,再用$f[i]$表示$i$这个取了8个的状态有多少种合法方案。因为最后的统计是独立的,所以对于总共16个数拆成的状态$i$和状态$j$来说,它们对答案的贡献是$f[i]*f[j]$。
再来考虑如何计算$f[x]$。因为$x$这个状态是选了8个砝码的状态,最普通的计算当然就是$8!$地枚举有多少满足条件。毋庸置疑这里也可以用meet in middle来优化,具体就是用$mp[i][j]$表示$i$这个状态下权值为$j$的方案数。因为8选4之后的4个有$4!$的排列情况,每一种情况的权值都不一样。之后的操作就是比较经典的meet in middle模型了。算出来后再将答案贡献至$f[]$即可。
这是一个正确的算法,但是所要计算的状态数达到了$2*C_{16}^{8}*C_{7}^{4}*4!=21621600$。千万级别的状态数……还是吃不太消。
#include<bits/stdc++.h> int a[],s[],c[];
int mp[][],f[],bel;
bool vis[],tk[];
int scenario;
long long ans; void get(int x, int done, bool opt)
{
if (done==){
register int statu = , tt;
c[] = ;
for (int i=; i<x; i++)
if (tk[i]) statu += <<(i-), c[++c[]] = a[s[i]];
for (int i=; i<=; i++)
for (int j=; j<=; j++)
if (i!=j)
for (int k=; k<=; k++)
if (i!=k&&j!=k)
for (int l=; l<=; l++)
if (i!=l&&j!=l&&k!=l){
tt = i*c[]+j*c[]+k*c[]+l*c[];
if (!opt)
mp[statu][tt]++;
else f[bel] += mp[-statu][tt];
}
return;
}
if (x > ) return;
tk[x] = , get(x+, done+, opt);
tk[x] = , get(x+, done, opt);
}
void check()
{
memset(mp, , sizeof mp);
s[] = , bel = ;
for (int i=; i<=; i++)
if (vis[i]) s[++s[]] = i, bel += <<(i-);
get(, , );
tk[] = ;
get(, , );
tk[] = ;
}
void dfs(int now, int done, bool opt)
{
if (done==){
if (!opt) check();
else{
int status = ;
for (int i=; i<now; i++)
if (vis[i]) status += <<(i-);
ans += 1ll*f[status]*f[-status];
}
return;
}
if (now > ) return;
vis[now] = , dfs(now+, done+, opt);
vis[now] = , dfs(now+, done, opt);
}
int main()
{
while (scanf("%d",&a[])&&a[])
{
memset(f, , sizeof f);
for (int i=; i<=; i++) scanf("%d",&a[i]);
dfs(, , );
ans = ;
dfs(, , );
printf("Case %d: %lld\n",++scenario,ans);
}
return ;
}
高效合并信息
其实上一种做法最关键受限在于选出8个后再选4个。这里每次选4个都是相对独立的操作,而不能共享重复的信息。也就是说,我们其实可以一开始就只考虑选4个,再考虑合并成选8个。
用$f[x]$表示所有权值为$x$的选数方案,于是合并时就可以通过位运算来判断两个状态是否能够合并了。最后统计时也是一样,统计所有选了8个的状态就行了。
第二种相当于是从小信息合并回大信息,不仅时间复杂度优秀,理解和代码复杂度也十分轻松。
注意一下这样子的话,$a[]$数组是要先排序的,否则next_permutation无法遍历全部情况。
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm> std::vector<int> f[];
int bit[],a[],mp[],scenario;
long long ans; bool check(int x)
{
bit[] = ;
for (int i=; i<; i++)
if (x&(<<i))
bit[++bit[]] = a[i+];
return bit[]==;
}
int main()
{
while (scanf("%d",&a[])&&a[])
{
memset(mp, , sizeof mp);
for (int i=; i<=; i++) scanf("%d",&a[i]);
std::sort(a+, a+);
for (int i=; i<=; i++) f[i].clear();
for (int i=; i<=; i++)
if (check(i))
do{
int tt = bit[]*+bit[]*+bit[]*+bit[]*;
for (unsigned int j=; j<f[tt].size(); j++)
if ((i&f[tt][j])==) mp[i|f[tt][j]]++;
f[tt].push_back(i);
}while (std::next_permutation(bit+, bit+));
ans = ;
for (int i=; i<=; i++)
ans += 1ll*mp[i]*mp[^i];
printf("Case %d: %lld\n",++scenario,ans/);
}
return ;
}
真是很奇妙啊……
END
【状态压缩 meet in middle】poj3139Balancing the Scale的更多相关文章
- [poj3904]Sky Code_状态压缩_容斥原理
Sky Code poj-3904 题目大意:给你n个数,问能选出多少满足题意的组数. 注释:如果一个组数满足题意当且仅当这个组中有且只有4个数,且这4个数的最大公约数是1,$1\le n\le 10 ...
- POJ 1198 / HDU 1401 Solitaire (记忆化搜索+meet in middle)
题目大意:给你一个8*8的棋盘,上面有四个棋子,给你一个初始排布,一个目标排布,每次移动,可以把一个棋子移动到一个相邻的空位,或者跨过1个相邻的棋子,在保证棋子移动不超过8次的情况下,问能否把棋盘上的 ...
- POJ 3254. Corn Fields 状态压缩DP (入门级)
Corn Fields Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 9806 Accepted: 5185 Descr ...
- HDU 3605:Escape(最大流+状态压缩)
http://acm.hdu.edu.cn/showproblem.php?pid=3605 题意:有n个人要去到m个星球上,这n个人每个人对m个星球有一个选择,即愿不愿意去,"Y" ...
- [HDU 4336] Card Collector (状态压缩概率dp)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4336 题目大意:有n种卡片,需要吃零食收集,打开零食,出现第i种卡片的概率是p[i],也有可能不出现卡 ...
- HDU 4336 Card Collector (期望DP+状态压缩 或者 状态压缩+容斥)
题意:有N(1<=N<=20)张卡片,每包中含有这些卡片的概率,每包至多一张卡片,可能没有卡片.求需要买多少包才能拿到所以的N张卡片,求次数的期望. 析:期望DP,是很容易看出来的,然后由 ...
- codeforces B - Preparing Olympiad(dfs或者状态压缩枚举)
B. Preparing Olympiad You have n problems. You have estimated the difficulty of the i-th one as inte ...
- NOIP2005过河[DP 状态压缩]
题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数 ...
- vijos1426兴奋剂检查(多维费用的背包问题+状态压缩+hash)
背景 北京奥运会开幕了,这是中国人的骄傲和自豪,中国健儿在运动场上已经创造了一个又一个辉煌,super pig也不例外……………… 描述 虽然兴奋剂是奥运会及其他重要比赛的禁药,是禁止服用的.但是运动 ...
随机推荐
- javascript的学习笔记---复习及学习
1.javascript包含三大部分(BOM,DOM,ECMAscript) ECMAscript:规定js的语法规范 BOM:Document Object Model 给我们提供了一套完整的操作页 ...
- 黑马Lambda表达式学习 Stream流 函数式接口 Lambda表达式 方法引用
- 分布式通信-tcp/ip 单播
服务端 public class SingleBroadCastSocketServer { public static void main(String[] args) { ServerSocket ...
- Python 数字模块
Python中的数字模块 math模块 random模块 Decimal模块 - 没有损失的小数 Fraction模块 - 可以计算分数
- 【Codeforces1111D_CF1111D】Destroy the Colony(退背包_组合数学)
题目: Codeforces1111D 翻译: [已提交至洛谷CF1111D] 有一个恶棍的聚居地由几个排成一排的洞穴组成,每一个洞穴恰好住着一个恶棍. 每种聚居地的分配方案可以记作一个长为偶数的字符 ...
- 利用HtmlParser解析网页内容
一,htmpparser介绍 htmlparser是一个功能比较强大的网页解析工具,主要用于 html 网页的转换(Transformation) 以及网页内容的抽取 (Extraction). 二, ...
- JVM-GC日志分析
程序运行时配置如下参数: -Xms20M -Xmx20M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio= -XX:+PrintGC ...
- spring boot使用AbstractXlsView导出excel
一.maven依赖jar包 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi ...
- JMeter--PerfMon Metrics Collector监控内存及CPU
1.需要准备的软件及插件 ServerAgent-2.2.1.zip JMeterPlugins-Standard-1.3.1.zip 2.jmeter上JMeterPlugins-Standard- ...
- 理解C#系列 / 前言
前言 索引 写什么? 为什么写? 怎么写? 写什么? 写和C#编程相关的知识. 写知识的定义,附加对知识的理解. 写知识的作用,使用的场景,使用的条件. 写知识的本质,技术的结构,工作的原理. 写知识 ...