数组溢出真是可怕的事情

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的更多相关文章

  1. [poj3904]Sky Code_状态压缩_容斥原理

    Sky Code poj-3904 题目大意:给你n个数,问能选出多少满足题意的组数. 注释:如果一个组数满足题意当且仅当这个组中有且只有4个数,且这4个数的最大公约数是1,$1\le n\le 10 ...

  2. POJ 1198 / HDU 1401 Solitaire (记忆化搜索+meet in middle)

    题目大意:给你一个8*8的棋盘,上面有四个棋子,给你一个初始排布,一个目标排布,每次移动,可以把一个棋子移动到一个相邻的空位,或者跨过1个相邻的棋子,在保证棋子移动不超过8次的情况下,问能否把棋盘上的 ...

  3. POJ 3254. Corn Fields 状态压缩DP (入门级)

    Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 9806   Accepted: 5185 Descr ...

  4. HDU 3605:Escape(最大流+状态压缩)

    http://acm.hdu.edu.cn/showproblem.php?pid=3605 题意:有n个人要去到m个星球上,这n个人每个人对m个星球有一个选择,即愿不愿意去,"Y" ...

  5. [HDU 4336] Card Collector (状态压缩概率dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4336 题目大意:有n种卡片,需要吃零食收集,打开零食,出现第i种卡片的概率是p[i],也有可能不出现卡 ...

  6. HDU 4336 Card Collector (期望DP+状态压缩 或者 状态压缩+容斥)

    题意:有N(1<=N<=20)张卡片,每包中含有这些卡片的概率,每包至多一张卡片,可能没有卡片.求需要买多少包才能拿到所以的N张卡片,求次数的期望. 析:期望DP,是很容易看出来的,然后由 ...

  7. codeforces B - Preparing Olympiad(dfs或者状态压缩枚举)

    B. Preparing Olympiad You have n problems. You have estimated the difficulty of the i-th one as inte ...

  8. NOIP2005过河[DP 状态压缩]

    题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数 ...

  9. vijos1426兴奋剂检查(多维费用的背包问题+状态压缩+hash)

    背景 北京奥运会开幕了,这是中国人的骄傲和自豪,中国健儿在运动场上已经创造了一个又一个辉煌,super pig也不例外……………… 描述 虽然兴奋剂是奥运会及其他重要比赛的禁药,是禁止服用的.但是运动 ...

随机推荐

  1. idea ultmate版安装后toolWindows没有database

    点击Configure--->plugins将database勾选

  2. c++的substr()函数

    substr()函数注意:string str =“Hello”: substr(0,4)=“Hell”,0是起始位置,4是要复制的长度,strlen函数输出的是除了结束符"\0" ...

  3. Mysql 到 Hbase 数据如何实时同步,强大的 Streamsets 告诉你

    很多情况大数据集群需要获取业务数据,用于分析.通常有两种方式: 业务直接或间接写入的方式 业务的关系型数据库同步到大数据集群的方式 第一种可以是在业务中编写代码,将觉得需要发送的数据发送到消息队列,最 ...

  4. android webview.goback 问题

    重写 shouldOverrideUrlLoading 不需要实现 view.loadUrl(url);直接return false;即可 如果实现了,则使用window.location.repla ...

  5. 字符条件变成in条件格式数据

    private string getInQuerySql(string query) { string resulr = ""; foreach (var item in quer ...

  6. centos7安装chrome浏览器

    1.配置yum下载源: 在目录 /etc/yum.repos.d/ 下新建文件 google-chrome.repo, 并且在该文件中添加如下内容: [google-chrome]name=googl ...

  7. python 基础(八) os模块

    OS模块 概念:包含了普遍的操作 系统的功能 一.函数 函数名 函数说明 os.name 获取操作系统类型 nt->Windows posix->Linux/Unix os.listdir ...

  8. 洛谷P2514||bzoj2426 [HAOI2010]工厂选址

    洛谷P2514 bzoj2426 其实是个简单的贪心,然而不适合在脑子不清醒的时候做...看不懂题意续了1个小时 很容易发现应该枚举新建哪个发电厂,对于这种方案就是取其中b吨煤运到原来发电厂,取剩下( ...

  9. Java EE学习笔记(五)

    Spring事务管理 1.Spring事务管理概述 1).在实际开发中,操作数据库时都会涉及到事务管理问题,为此Spring提供了专门用于事务处理的API.(事务特性:ACID,原子性,一致性,隔离性 ...

  10. python学习之调试 错误捕捉及处理

    1 捕捉错误:try except    Err_Case1: pass except    Err_Case2: pass else: 正常情况: finally: 无论是否异常都要 处理的代码 w ...