二进制
状压在很多时候都是通过二进制表示集合状态,然后状态转移。
比如3头牛ABC,
取A(001),取B(010),取C(100)
取AB(011),取BC(110),取AC(101)
取ABC(111)。
位运算操作
1、将a的第k位修改为1:a |= 1《《k;
2、将a的第k位修改为0:a &= ~(1《《k);
3、取第k位:a>>k & 1;
4.a,b两个集合合并,c=a|b.
5.用于枚举子集:for ( x = S; x ; x = S & ( x - 1 ) ) ;!!!
二进制例题
题面
光棍组织
统计
描述
提交
自定义测试
题目描述
MM 虽然一辈子只要一个,但是也得早点解决。于是,n 个光棍们自发组成了一个光棍组织 (ruffian organization,By Wind 乱译)。现在,光棍们打算分成几个小组,并且分头为 找 MM 事
业做贡献(For example:searching,hunting……By Wind 乱译)。 对于这 n 个光棍的任意一个组合,都有一个被称为“和谐度”的东西,现在,他们想知道, 如何分组
可以使和谐度总和最大。 每个光棍都必须属于某个分组,可以一个人一组。
输入格式
第 1 行为 n,接下来 2^n-1 行,按照 2 进制给出每个分组的和谐度。
(比如接下来第 5 行,也就是总共第六行,2 进制为 00000101,则表示第 1 个人和第 3 个人 这个分
组的和谐度,第 31 行则为 1~5 在一起的和谐度)
输出格式
仅 1 行,为最大和谐度和。
样例数据
input
3
41
12
57
94
89
23
12
output
151
解析
二进制表示状态,枚举各种子集分布情况。
然后求出最大值。
代码
#include<bits/stdc++.h>
using namespace std;
int x,n;
int dp[100010]={};
int main()
{
scanf("%d",&n);
for(int i=1;i<1<<n;++i)
scanf("%d",&dp[i]);
for(int i=1;i<1<<n;++i)
for(int j=i&(i-1);j;j=i&(j-1))
dp[i]=max(dp[i],dp[i-j]+dp[j]);
printf("%d",dp[(1<<n)-1]);
return 0;
}
状压
通过二进制表示状态后,就可以通过不同阶段的状态进行转移了。
例题
种花小游戏
统计
描述
提交
自定义测试
题目描述
植物大战僵尸这款游戏中,还有个特别有意思的赚钱方式——种花(能长金币的花)。 种出来的金币需要玩家点击才能得到,或者,玩家可以购买一只蜗牛来帮助捡金币。然而,蜗牛爬得
慢是众所周知的。所以,场上有若干金币时,蜗牛总是喜欢以最少的行程来捡走所有的金币。 现在告诉你场上n个金币所在位置的坐标,以及蜗牛所在位置,让你求出蜗牛捡走所有金币的最小行程
。
输入格式
第一行一个正整数n,表示金币数量 之后n行,每行两个非负整数x、y,分别表示金币所在位置坐标 最后一行两个正整数x、y表示蜗牛起始位置。
输出格式
一个实数(保留2位小数),表示最短行程
样例数据
input
4
0 1
1 1
1 0
2 2
0 0
output
4.83
说明:(0,0)?(1,0)?(0,1)?(1,1)?(2,2) 1 + 1.414 + 1 + 1.414 = 4.83
解析
dp[s][j] 。s表示二进制状态,通常是转为十进制的数。j表示最后经过的那个点。
易得j是属于s的。
而且在s-j中找到一个k,就可以通过dp[s-j][k]推到dp[s][j]。
代码
#include<bits/stdc++.h>
using namespace std;
int n;
int x[20],y[20]={},num[100000]={};
double cost[20][20]={},dp[100000][20]={};
int main()
{
scanf("%d",&n);
num[1]=1;
for(int i=1;i<=n;++i)
scanf("%d%d",&x[i],&y[i]),num[1<<i]=i+1;
scanf("%d%d",&x[0],&y[0]);
for(int i=0;i<=n;++i)
for(int j=i+1;j<=n;++j)
cost[j][i]=cost[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
for(int i=1;i<1<<n;++i)
for(int j=1;j<=n;++j)
dp[i][j]=2000000000.00;
for(int i=1;i<=n;++i)
{
dp[1<<(i-1)][i]=cost[0][i];
for(int j=1;j<=n;++j)
if (i!=j)
dp[(1<<(i-1))+(1<<(j-1))][j]=min(dp[(1<<(i-1))+(1<<(j-1))][j],dp[1<<(i-1)][i]+cost[i][j]);
}
for(int i=1;i<1<<n;++i)
for(int j=i;j;j-=j&-j)
for(int k=i-(j&-j);k;k-=k&-k)
dp[i][num[j&-j]]=min(dp[i][num[j&-j]],dp[i-(j&-j)][num[k&-k]]+cost[num[k&-k]][num[j&-j]]);
double ans=2000000000.00;
for(int i=1;i<=n;++i)
ans=min(dp[(1<<n)-1][i],ans);
printf("%.2lf",ans);
return 0;
}