状态压缩DP

状态压缩,就是用二进制来作DP的状态,用位运算来转移状态

二进制操作

左移

x<<y表示将二进制x的每一位向左移y位,在末位补0

例:

001011<<2=010110<<1=101100

x<<y=x*2^y

右移

x>>y表示将二进制x的每一位向右移y位,末位为1时舍去

例:

01010>>2=00101>>1=00010

由于会舍去末位1,等同于整除运算

x>>y=x/2^y

与运算

x&y表示对于x与y的每一位都进行与运算

x=10010

y=100=00110

x|y=00010

或运算

x|y表示对于x与y的每一位都进行或运算

x=10010

y=100=00100

x|y=10110

非运算

~x表示对x的每一位取反

x=10010

~x=01101

异或运算

x^y表示对x与y的每一位都进行异或运算

如果两位不同,则为1,否则为0

x=1001

y=111=0111

x^y=1110

较复杂的位运算常用操作

枚举n的子集:

for (int i=n;i;i=n&(i-1))

将x的第i位变成0:

x=x&(~(1<<(i-1)))

将x的第i位取出:

x=x&(1<<(i-1))

状态压缩例题

光棍组织

题目描述

MM 虽然一辈子只要一个,但是也得早点解决。于是,n 个光棍们自发组成了一个光棍组织 。

现在,光棍们打算分成几个小组,并且分头为 找 MM 事业做贡献。

对于这 n 个光棍的任意一个组合,都有一个被称为“和谐度”的东西,现在,他们想知道, 如何分组

可以使和谐度总和最大。 每个光棍都必须属于某个分组,可以一个人一组。

输入格式

第 1 行为 n,接下来 2^n-1 行,按照 2 进制给出每个分组的和谐度。

输出格式

仅 1 行,为最大和谐度和。

样例数据

input

3
41
12
57
94
89
23
12

output

151

数据规模与约定

数据范围 对于 30%数据,n<=5;

对于 100%数据 n<=16,1<=每个组的和谐度<=1000000,输入均为整数。

解析

可以先设:dp[i]为在i(一个01串,每一位是1则表示选择,0表示不选择)时的最大和谐度。然后枚举i的子集,就可以通过i的子集拆分为两个子集进行决策了

inline void work()
{
	for (int i=1;i<=n;i++)//注意程序中的n已经赋为(1<<n)-1
	 dp[i]=read();//对dp数组的初始化,
	for (int i=1;i<=n;i++)//枚举i
	 for (int j=i&(i-1);j;j=i&(j-1))//枚举i的子集
	  dp[i]=max(dp[i],dp[j]+dp[i-j]);//j与i-j两个i的子集可以合并为i
	printf("%d\n",dp[n]);//输出
}

种花小游戏

题目描述

植物大战僵尸这款游戏中,还有个特别有意思的赚钱方式——种花(能长金币的花)。 种出来的金币需要玩家点击才能得到,或者,玩家可以购买一只蜗牛来帮助捡金币。然而,蜗牛爬得慢是众所周知的。

所以,场上有若干金币时,蜗牛总是喜欢以最少的行程来捡走所有的金币。 现在告诉你场上n个金币所在位置的坐标,以及蜗牛所在位置,让你求出蜗牛捡走所有金币的最小行程

输入格式

第一行一个正整数n,表示金币数量 之后n行,每行两个非负整数x、y,分别表示金币所在位置坐标 最后一行两个正整数x、y表示蜗牛起始位置。

输出格式

一个实数(保留2位小数),表示最短行程

样例数据

input

4
0 1
1 1
1 0
2 2
0 0

output

4.83

数据规模与约定

对于20%的数据

n=3对于70%的数据

n<=8对于100%的数据

n<=16 x、y<=10000

解析

可以先设:dp[i][j]为在i(一个01串,每一位是1则表示已经捡取,0表示未捡取)并且当前在第j个地点时的最短路径。

先预处理出任两点i,j之间的距离dis[i][j]=dis[j][i],则得到:

dp[i][j]=f[i-(1<<(j-1))][k]+dis[j][k]

就可以写出代码了:

inline void work()
{
    for (int i=1;i<=m;i++)//m=1<<n-1
     for (int j=1;j<=n;j++)
      for (int k=1;k<=n;k++)
       if ((j^k || ~i-(1<<j-1)) && ((i>>j-1)&1) && ((i>>k-1)&1))
        dp[i][j]=min(dp[i][j],dp[i-(1<<j-1)][k]+dis[j][k]);
    double ans=2e9;
    for (int i=1;i<=n;i++)
     ans=min(ans,dp[m][i]);
    printf("%.2lf\n",ans);
}



猜你喜欢

转载自blog.csdn.net/fsl123fsl/article/details/80463838
今日推荐