【状态压缩 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也不例外……………… 描述 虽然兴奋剂是奥运会及其他重要比赛的禁药,是禁止服用的.但是运动 ...
随机推荐
- 调用Web API将文件上传到服务器的方法(.Net Core)
最近遇到一个将Excel通过Web API存到服务器的问题,其中涉及到Excel的读取.调用API.Web API怎么进行接收. 一. Excel的读取.调用API Excel读取以及调用API的代 ...
- 分布式通信-tcp/ip socket
Socket通讯的过程 Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,Server端向Client端发回Accept(接受) ...
- js截取文件名不带后缀
利用正则表达式是匹配后缀名 一般文件后缀都为.xxx,也就是说从尾部匹配的话首先是字母,接着应该有一个. 那么我们就开始写正则表达式 利用在线工具 方便我们编写边测试 一般都是小写字母,所以我们用[a ...
- TopCoder9915(期望dp)
1.还是逆向. 2.状态是还剩红i黑j张时的期望,这样从0,0往R,B推.注意因为是逆着的,所以到了某一步发现期望为负时直接f[i][j]归零,意义是这之后(在递推中算是这之前)的都不摸了,到这就停( ...
- Gym - 101810F ACM International Collegiate Programming Contest (2018)
bryce1010模板 http://codeforces.com/gym/101810 #include<bits/stdc++.h> using namespace std; #def ...
- bzoj2818: Gcd懵逼乌斯反演
由于是单组数据,强行不分块O(n)过 线性筛部分非常神奇,用了一个奇妙的推导(懒得写了) #include <bits/stdc++.h> using namespace std; ],f ...
- 关于在ARM MDK 中使用STM32F4xx 硬件浮点单元的话题
http://mp.weixin.qq.com/s/CDyZ8v2kLiyuIBHf7iqEPA
- css水平垂直居中块整理
1.绝对定位+负margin 兼容性很好,但需要指定子块的高度和宽度,以及负margin .wp{ position: relative; width: 200px; height: 200px; b ...
- Crusher Django 学习笔记2 基本url配置
http://crusher-milling.blogspot.com/2013/09/crusher-django-tutorial2-conf-basic-url.html 顺便学习一下FQ Cr ...
- Access denied for user ''@'localhost' to database 的一个问题
$conn = new mysqli("127.0.0.1", 'abc', '', DB_DATABASE); 在提供了用户名的情况下,竟然返回错误 说用户提供的用户为空,非常奇 ...