【状态压缩 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也不例外……………… 描述 虽然兴奋剂是奥运会及其他重要比赛的禁药,是禁止服用的.但是运动 ...
随机推荐
- Django (七) token&静态文件&媒体文件
token&静态文件&媒体文件 1. token 1. 会话技术 2. 服务端会话技术 3. 它实际上就是手动实现的session 4. 实现token 4.1 在models.py中 ...
- python学习之图形界面编程:
一 tkinter:tkinter是python自带的支持tk的库,python代码调用tkinter->tk->操作系统提供的本地GUI(TKL语言开发))完成界面开发,不需要安装任何第 ...
- opencart 安装
1:安装 php5 apache2 mysql 2:下载opencart wget https://github.com/opencart/opencart/archive/master.zi ...
- qq登录,新浪微博登录接口申请过程中遇到的问题
接口申请下来了,开发很容易的,参数传到就可以了.以前就做过这方面的开发,但是申请还是第一次,网上有关这方面的东东不是很多,所以记录一下申请过程. 1,qq登录接口申请 申请地址是:http://con ...
- jquery测试解析
1.下列获取元素范围大小顺序错误的是 (选择一项) 1 A: B: C: D: 本题选择D 解析: 获取元素范围大小顺序依次为: $(#one).siblings("div")&g ...
- 搭建高可用mongodb集群—— 副本集
转自:http://www.lanceyan.com/tech/mongodb/mongodb_repset1.html 在上一篇文章<搭建高可用MongoDB集群(一)——配置MongoDB& ...
- git与GitHub(一)
相信,很多初入前端者都会对git以及GitHub不太了解,而我当时也经历过各种面试大关,也都会问:你了解git和GitHub吗?那么今天先来说一说git. 那么什么是git? (以下转载自廖雪峰老师的 ...
- [BZOJ1047][HAOI2007]理想的正方形 二维单调队列
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1047 我们对每矩阵的一列维护一个大小为$n$的单调队列,队中元素为矩阵中元素.然后扫描每一 ...
- Android 检查内存溢出
工具网址:https://github.com/square/leakcanary 中文版说明地址:http://www.liaohuqiu.net/cn/posts/leak-canary-read ...
- Chisel语言
1 What is Chisel? Chisel(Constructing Hardware In a Scala Embedded Language)是一种嵌入在高级编程语言Scala的硬 ...