有的DP题,某一部分的状态只有两种,选或不选。

开数组记录,代价太大,转移不方便。

状态压缩意为,用 “0/1“ 表示 “选/不选“ 。

把状态表示为二进制整数。

There are 10 kinds of people in the world, who knows binary and who doesn't.

用位运算判断条件并转移状态。

hdu 6149 Valley Numer II

题目传送门

用f[i][j]表示选到前i个点,状态为j的答案。

枚举其他两个高点。

转移之前判断之前是否用过,以及高低点之间是否有连边。

所以用邻接矩阵表示连边比较方便。

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; int t;
int n,m,k;
int c[][];
int h[];
int v[];
int f[][(<<)+];
int ans; int main()
{
scanf("%d",&t);
while(t--)
{
memset(f,,sizeof(f));
memset(c,,sizeof(c));
memset(h,,sizeof(h));
memset(v,,sizeof(v));
scanf("%d%d%d",&n,&m,&k);
for(int i=;i<=m;i++)
{
int q,w;
scanf("%d%d",&q,&w);
c[q][w]=;
c[w][q]=;
}
for(int i=;i<k;i++)
{
scanf("%d",&h[i]);
v[h[i]]=;
}
int st=(<<k);
ans=;
for(int i=;i<=n;i++)
{
for(int j=;j<st;j++)f[i][j]=f[i-][j];
if(v[i])continue;
for(int j=;j<st;j++)
{
for(int q1=;q1<k;q1++)
{
if(j&(<<q1))continue;
if(!c[i][h[q1]])continue;
for(int q2=;q2<q1;q2++)
{
if(j&(<<q2))continue;
if(!c[i][h[q2]])continue;
f[i][(j|(<<q1))|(<<q2)]=max(f[i][(j|(<<q1))|(<<q2)],f[i-][j]+);
}
}
}
}
for(int j=;j<st;j++)ans=max(ans,f[n][j]);
printf("%d\n",ans);
}
return ;
}

hdu 6149 Valley Numer II

CodeForces 895C Square Subsets

题目传送门

一道数学思想浓重的状压DP。

因为1~70只有19个质数(用pr[]储存每个质数),先预处理出每个数的质因子组成c。

c[i]的第k位表示i这个数含有几个pr[k]因子。含有奇数个,那一位就是1,,否则为零。

f[i][j]表示选到第i个数,此时所有选的数的积的质因子组成为j的时候的方案数。

那个“组成”与之前预处理的表示方法类似,每一个二进制位代表一个质因子的情况,0为偶数个,1为奇数个。

读入的时候记录每个数被读进来的次数。

如果读入的数据中含有x这个数,就对x进行一次计算。

具体来说,x可以选奇数个也可以选偶数个。

偶数个的情况状态不变。奇数个的情况,状态异或上x这个数的组成c[x]。

两个加一起,乘上方案数,取个模即可。

最后因为要求是平方数,所以所有质因子都有偶数个,状态为零。

输出f[nw][0]。

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 1000000007
#define ll long long
using namespace std; int n;
int h[];
int c[];
int pr[]={,,,,,,,,,,,,,,,,,,,};
ll f[][<<];
ll pow[];
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)
{
int t;
scanf("%d",&t);
h[t]++;
}
for(int i=;i<=;i++)
{
int t=i;
int p=;
while(t>)
{
while(t%pr[p]==)
{
t/=pr[p];
c[i]^=(<<p);
}
p++;
}
}
pow[]=;
for(int i=;i<=n;i++)pow[i]=(pow[i-]<<)%mod;
f[][]=;
int nw=;
for(int i=;i<=;i++)
{
if(!h[i])continue;
nw^=;
for(int j=;j<(<<);j++)
{
f[nw][j]=(f[nw^][j^c[i]]+f[nw^][j])%mod*pow[h[i]-]%mod;
}
}
printf("%d",f[nw][]-);
return ;
}

CodeForces 895C Square Subsets

CodeForces 482C Game with Strings

题目传送门

一道期望+状压DP......

摧残大脑的推导与状态转移。

强荐zhx大佬的题解,写的非常非常明白。zhx大佬的题解传送门

作为蒟蒻我就只能大致解释解释代码了QwQ

f[i]代表问了状态为i的问题时,距离确定字符串(终点结果)的期望次数。

显然,f[一个能确定字符串的问法]=0。

接下来的难点在于如何判断每种问法不能确定哪些字符串。

设dbt[i]代表问法为i时,不能区分的字符串有哪些。

用二进制数表示,每一位代表一个字符串,1代表不能确定,0反之。

num[i]代表问法为i时,不能区分的串的个数,也就是dbt[i]中1的个数。

接下来玄学公式和位运算转移状态,zhx大佬的题解里写的很详细,本蒟蒻在此不做赘述。

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std; int n,m;
char s[][];
ll dbt[<<];
int num[<<];
double f[<<]; int main()
{
scanf("%d",&n);
for(int i=;i<n;i++)
{
scanf("%s",s[i]);
}
if(n==){printf("0.000000000");return ;}
m=strlen(s[]);
for(int i=;i<n;i++)
{
for(int j=i+;j<n;j++)
{
ll st=;
for(int k=;k<m;k++)
{
if(s[i][k]==s[j][k])st|=(1ll<<k);
}
dbt[st]|=(1ll<<i);
dbt[st]|=(1ll<<j);
}
}
for(int i=(<<m)-;i>=;i--)
{
for(int j=;j<m;j++)
{
if(i&(<<j))dbt[i^(<<j)]|=dbt[i];
}
}
for(int i=;i<(<<m);i++)
{
for(int j=;j<n;j++)
{
if(dbt[i]&(1ll<<j))num[i]++;
}
}
f[(<<m)-]=0.00;
for(int i=(<<m)-;i>=;i--)
{
if(!num[i]){f[i]=0.00;continue;}
int tot=m;
for(int j=;j<m;j++)
{
if(i&(<<j))tot--;
}
for(int j=;j<m;j++)
{
if(i&(<<j))continue;
f[i]+=f[i|(<<j)]*1.0/(double)(tot)*(double)(num[i|(<<j)])/(double)(num[i]);
}
f[i]+=1.00;
}
printf("%.9lf",f[]);
return ;
}

CodeForces 482C Game with Strings

hdu 6125 Free from square

题目传送门

由于对于一个合法的答案,sqrt(500)后的素数最多只会用到一个,所以只对前8个素数状压后01背包。

f(S, k)表示素数状态为S时由k个自然数组成的方法有几种。

再对后面的素数分别01背包。

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 1000000007
#define ll long long
using namespace std; int t,n,K;
int num[<<];
bool pr[];
ll f[<<][];
ll ans; int main()
{
num[]=;
int k=;
for(int i=;i<;i++)
{
if(!pr[i])
{
if(k<)num[<<k]=i,k++;
for(int j=i<<;j<;j+=i)pr[j]=;
}
}
for(int i=;i<(<<);i++)
{
int j=(i-)&i;
num[i]=num[j]*num[i^j];
if(num[i]>)num[i]=;
}
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&K);
memset(f,,sizeof(f));
f[][]=;
for(int i=;i<(<<);i++)
{
if(num[i]<=n)
{
int ss =((<<)-)^i;
for(int k=K;k>=;k--)
for(int j=ss;;j=(j-)&ss)
{
f[i|j][k]=(f[i|j][k]+f[j][k-])%mod;
if(j==)break;
}
}
}
for(int i=;i<=n;i++)
{
if(pr[i])continue;
for(int k=K;k>;k--)
{
for(int j=;j<(<<);j++)
{
if(num[j]*i<=n)
{
int ss=((<<)-)^j;
for(int s=ss;;s=(s-)&ss)
{
f[j|s][k]=(f[s][k-]+f[j|s][k])%mod;
if(s==)break;
}
}
}
}
}
ans=;
for(int i=;i<(<<);i++)
for(int k=K;k>=;k--)
ans=(ans+f[i][k])%mod;
printf("%lld\n",ans);
}
return ;
}

hdu 6125 Free from square

CodeForces 449D Jzzhu and Numbers

题目传送门

传说中的官方题解:

(如果图看起来长宽比不对,查看原图即可)

想法不是很简单。

脑洞大,真的想不出来。

看了题解瞎打了一会,cf测,过不了test5。

盯着code看了半个小时,发现是a数组开小了,少打了个0。

话说a数组本来也没什么用是吧。

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define mod 1000000007
using namespace std; int n;
int a[];
int f[<<][]; ll ksm(int B,int p)
{
ll ret=;
ll b=B;
while(p)
{
if(p&)ret=(ret*b)%mod;
b=(b*b)%mod;
p>>=;
}
return ret;
} int cnt(int x)
{
int ret=;
while(x)x-=(x&(-x)),ret++;
return (ret&)?-:;
} int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)scanf("%d",&a[i]),f[a[i]][]++;
for(int i=;i<=;i++)
{
for(int j=;j<(<<);j++)
{
if(&(j>>(i-)))f[j][i]=f[j][i-];
else f[j][i]=f[j][i-]+f[j+(<<(i-))][i-];
}
}
ll ans=;
for(int i=;i<(<<);i++)
{
ll tmp=((ksm(,f[i][])-)*cnt(i)%mod+mod)%mod;
ans=(ans+tmp+mod)%mod;
}
printf("%I64d",ans);
return ;
}

CodeForces 449D Jzzhu and Numbers

hdu 5713 K个联通块

题目传送门

跟前面某些题相比还是比较友善的。

状态不是很难想,转移也是显然,就是中间的步骤比较多,倒起来有些复杂。

首先算出num[i]表示选点为i状态时,i中这些点相互连边的方案数。

具体做法是找出lowbit代表的那个点,跟前面的点一一计算累加到答案上。

再算出f[i][p]代表所选点集为i时,构成p个联通块的方案数。

第一步要算出f[i][1]。

利用容斥原理,用所有的方法减去不合法的方法。

再利用f[i][1]算出f[i][2...k]。

最后的答案为f[所有点都选((1<<n)-1)][k]。

注意取模。

模数很奇葩。

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define mod 1000000009
using namespace std; int t,n,m,k;
int e[][],num[<<];
ll f[<<][]; int count(int nw,int p)
{
int ret=;
for(int i=;i<=n;i++)
{
if((<<(i-))!=p)continue;
p=i;
break;
}
for(int i=;i<=n;i++)
{
if(nw&(<<(i-)))ret+=e[i][p];
}
return ret;
} int main()
{
scanf("%d",&t);
for(int cs=;cs<=t;cs++)
{
memset(e,,sizeof(e));
memset(num,,sizeof(num));
memset(f,,sizeof(f));
scanf("%d%d%d",&n,&m,&k);
for(int i=;i<=m;i++)
{
int v1,v2;
scanf("%d%d",&v1,&v2);
e[v1][v2]=e[v2][v1]=;
}
for(int i=;i<(<<n);i++)
{
int lb=i&(-i);
num[i]=num[i^lb]+count(i,lb);
}
for(int i=;i<(<<n);i++)
{
int lb=i&(-i);
ll no=;
for(int j=i^lb;j;j=((j-)&(i^lb)))
{
no=(no+f[i^j][]*1ll%mod*(1ll<<num[j])%mod)%mod;
}
f[i][]=(1ll*(1ll<<num[i])%mod-no)%mod;
}
for(int i=;i<(<<n);i++)
{
for(int j=;j<=k;j++)
{
int lb=i&(-i);
for(int h=i^lb;h;h=((h-)&(i^lb)))
{
f[i][j]=(f[i][j]+f[h][j-]*f[i^h][]%mod)%mod;
}
}
}
printf("Case #%d:\n%lld\n",cs,(f[(<<n)-][k]%mod+mod)%mod);
}
return ;
}

hdu 5713 K个联通块

状压DP小拼盘的更多相关文章

  1. bzoj3380: [Usaco2004 Open]Cave Cows 1 洞穴里的牛之一(spfa+状压DP)

    数据最多14个有宝藏的地方,所以可以想到用状压dp 可以先预处理出每个i到j的路径中最小权值的最大值dis[i][j] 本来想用Floyd写,无奈太弱调不出来..后来改用spfa 然后进行dp,这基本 ...

  2. [poj2411] Mondriaan's Dream (状压DP)

    状压DP Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One nigh ...

  3. ZOJ3802 Easy 2048 Again (状压DP)

    ZOJ Monthly, August 2014 E题 ZOJ月赛 2014年8月 E题 http://acm.zju.edu.cn/onlinejudge/showProblem.do?proble ...

  4. HDU 1565&1569 方格取数系列(状压DP或者最大流)

    方格取数(2) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total S ...

  5. 【62测试】【状压dp】【dfs序】【线段树】

    第一题: 给出一个长度不超过100只包含'B'和'R'的字符串,将其无限重复下去. 比如,BBRB则会形成 BBRBBBRBBBRB 现在给出一个区间[l,r]询问该区间内有多少个字符'B'(区间下标 ...

  6. 2014 Super Training #1 B Fix 状压DP

    原题: HDU 3362 http://acm.hdu.edu.cn/showproblem.php?pid=3362 开始准备贪心搞,结果发现太难了,一直都没做出来.后来才知道要用状压DP. 题意: ...

  7. HITOJ 2662 Pieces Assignment(状压DP)

    Pieces Assignment My Tags   (Edit)   Source : zhouguyue   Time limit : 1 sec   Memory limit : 64 M S ...

  8. Codeforces Gym 100015F Fighting for Triangles 状压DP

    Fighting for Triangles 题目连接: http://codeforces.com/gym/100015/attachments Description Andy and Ralph ...

  9. hdu 4739 状压DP

    这里有状态压缩DP的好博文 题目:题目比较神,自己看题目吧 分析: 大概有两种思路: 1.dfs,判断正方形的话可以通过枚举对角线,大概每次减少4个三角形,加上一些小剪枝的话可以过. 2.状压DP,先 ...

随机推荐

  1. Powershell 中的管道

    管道 上个命令中的输出,通过管道作为下个命令的输入.Linux中的管道传递的是text,但ps中传递的是object.但是命令究竟返回的是什么类型呢?以下命令回答了这个问题: get-service ...

  2. rabbitmq文档

    https://blog.csdn.net/hellozpc/article/details/81436980

  3. Python笔记_第一篇_面向过程_第一部分_9.Ubuntu基础操作

    第一部分   Ubuntu简介 Ubuntu(乌班图)是一个机遇Debian的以桌面应用为主的Linux操作系统,据说其名称来自非洲南部祖鲁语或科萨语的“Ubuntu”一词,意思是“人性”.“我的存在 ...

  4. gcc -c xx.c 选项讲解

    -c选项表示编译.汇编指定的源文件(也就是编译源文件),但是不进行链接.使用-c选项可以将每一个源文件编译成对应的目标文件. 目标文件是一种中间文件或者临时文件,如果不设置该选项,gcc 一般不会保留 ...

  5. 正则表达式匹配字符串中的数字 Python

    1.使用“\d+”匹配全数字 代码: import re zen = "Arizona 479, 501, 870. Carlifornia 209, 213, 650." m = ...

  6. psi 函数计算

    scipy.special.psi odps中不支持 scipy.special.psi,需要改写 基于 chebyshev_polynomial https://people.sc.fsu.edu/ ...

  7. C#匿名委托,匿名函数,lambda表达式

    一.类型.变量.实例之间的关系. 类型>变量>实例 类型可以创建变量,实体类可以创建实例,实例可以存储在变量里. 二.委托使用过程: 1.定义委托(写好签名): 2.创建委托变量: 3.给 ...

  8. 拾起HTML+CSS的一天

    今天在w3school看了下HTML5和CSS3的新特性. 本来觉得自己对CSS方面基础还挺有把握的,就之前自学都是跳过这个模块的,但经过昨天会友的面试,感觉自己好像太忽视基础了,很多基本的东西很模糊 ...

  9. Opencv笔记(七)——访问与操作像素

    一.获取矩阵的元素 1.获取三维矩阵img[i,j]处的元素 (b,g,r) = image[i,j],image大小为:MxNxK. 2.获取三维矩阵的子矩阵的全部元素 newimage = ima ...

  10. 大道至简伪代码读后感java为代码形式

    //愚公移山 import.java.大道至简.*; import.java.愚公移山.*; public class yishan //定义一个名为yishan的类 {//类定义的开始 public ...