有的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. c语言中assert的用法

    /************************************************************************* > File Name: assert.c ...

  2. Go-map-字符串-指针-结构体

    Maps 什么是 map ? 类似Python中的字典数据类型,以k:v键值对的形式. map 是在 Go 中将值(value)与键(key)关联的内置类型.通过相应的键可以获取到值. 如何创建 ma ...

  3. oracle sql语句学习(一)

    oraclexe 11.0.2.0 输出到文件 SQL>spool /*完整路径*/; SQL>spool off; 多表自然链接 select spj.sno from spj join ...

  4. polyA|ribo-minus|differentiated cell|Genetic heterogeneity

    转录组 测量单cell,可以认为是一种细胞.细胞株也认为来自同一个细胞. 使用两种方法,找mRNA(polyA)及rmRNA(ribo-minus),然后取交集. 转录组受实验影响,比如小片段没得到. ...

  5. Python笔记_第三篇_面向对象_2.构造函数和析构函数(含self说明)

    1. 构造函数: 为什么要有构造函数? 打一个比方:类的创建就是好比你创建了好了一种格式的房间,你租给上一个住户的后,里面会对方很多“垃圾”和不规则的物品摆放.构造函数就是下一个住户再使用的时候进行物 ...

  6. elasticsearch-hadoop 扩展定制 官方包以支持 update upsert doc

    官方源码地址https://github.com/elastic/elasticsearch-hadoop 相关文档 https://www.elastic.co/guide/en/elasticse ...

  7. 线程池-进程池-io模型

    一.线程池与进程池 什么是池?简单的说就是一个容器,一个范围 在保证计算机硬件安全的情况下最大限度的充分利用计算机, 池其实是降低了程序的运行效率,但是保证了计算机硬件的安全,也是实现了一个并发的效果 ...

  8. python基础——认识(if __name__ == ‘__main__’:)

    我们在写代码时,经常会用到这一句:if __name__ == '__main__',那么加这一句有什么用呢?实际上,它起到到了一个代码保护功能,它能够让别人在导入你写的模块情况下,无法看到和运行if ...

  9. Flume(二) —— 自定义拦截器、Source、Sink

    自定义拦截器 自定义Source 自定义Sink 引入依赖 <dependency> <groupId>org.apache.flume</groupId> < ...

  10. XEN 3166

    XEN 3166 这题原题是spj,校oj上只用判断yes no,不过也差不多 题意分析之后就是求两个东西: 字典序最小的长度为m的子序列 同时这个字典序严格大于某个字符串 用序列自动机 先尽量相同, ...