状压DP小拼盘
有的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小拼盘的更多相关文章
- bzoj3380: [Usaco2004 Open]Cave Cows 1 洞穴里的牛之一(spfa+状压DP)
数据最多14个有宝藏的地方,所以可以想到用状压dp 可以先预处理出每个i到j的路径中最小权值的最大值dis[i][j] 本来想用Floyd写,无奈太弱调不出来..后来改用spfa 然后进行dp,这基本 ...
- [poj2411] Mondriaan's Dream (状压DP)
状压DP Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One nigh ...
- ZOJ3802 Easy 2048 Again (状压DP)
ZOJ Monthly, August 2014 E题 ZOJ月赛 2014年8月 E题 http://acm.zju.edu.cn/onlinejudge/showProblem.do?proble ...
- HDU 1565&1569 方格取数系列(状压DP或者最大流)
方格取数(2) Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total S ...
- 【62测试】【状压dp】【dfs序】【线段树】
第一题: 给出一个长度不超过100只包含'B'和'R'的字符串,将其无限重复下去. 比如,BBRB则会形成 BBRBBBRBBBRB 现在给出一个区间[l,r]询问该区间内有多少个字符'B'(区间下标 ...
- 2014 Super Training #1 B Fix 状压DP
原题: HDU 3362 http://acm.hdu.edu.cn/showproblem.php?pid=3362 开始准备贪心搞,结果发现太难了,一直都没做出来.后来才知道要用状压DP. 题意: ...
- HITOJ 2662 Pieces Assignment(状压DP)
Pieces Assignment My Tags (Edit) Source : zhouguyue Time limit : 1 sec Memory limit : 64 M S ...
- Codeforces Gym 100015F Fighting for Triangles 状压DP
Fighting for Triangles 题目连接: http://codeforces.com/gym/100015/attachments Description Andy and Ralph ...
- hdu 4739 状压DP
这里有状态压缩DP的好博文 题目:题目比较神,自己看题目吧 分析: 大概有两种思路: 1.dfs,判断正方形的话可以通过枚举对角线,大概每次减少4个三角形,加上一些小剪枝的话可以过. 2.状压DP,先 ...
随机推荐
- slideshare文档下载
if [ x"$1" = x1 ]; then for i in {1..46}; do url_i="https://image.slidesharecdn.com/b ...
- java使用io流读取windows文件乱码问题
出现原因: 在IDEA中,使用 FileReader 读取项目中的文本文件.由于IDEA的设置,都是默认的 UTF-8 编码,所以没有任何 问题. 但是,当读取Windows系统中创建的文本文件时,由 ...
- UI Automation编程辅助工具Inspect的下载和使用
UIAutomation微软提供的UI自动化库,主要用AutomationElement类来表示UI 自动化目录树中的一个UI自动化元素,.NET Windows的窗体应用程序和WPF应用程序. In ...
- C/S 和 B/S架构
C/S 和 B/S架构 一.单机架构 应用领域: 植物大战僵尸 office 二.C/S架构 [ 应用领域: QQ 大型网络游戏 计算机发展初期用户去取数据,直接就去主机拿,从这里开始就分出了客户端和 ...
- Python程序中的线程操作(线程池)-concurrent模块
目录 Python程序中的线程操作(线程池)-concurrent模块 一.Python标准模块--concurrent.futures 二.介绍 三.基本方法 四.ProcessPoolExecut ...
- 在python实现加密的方式总结
基础知识扫盲 对称加密 对称密钥加密 , 又叫私钥加密.即信息发送的方和接受方用一个密钥去加密和揭秘数据. 最大的优势是 加解密速度快,适合对大量数据进行加密, 对称加密的缺点是密钥的管理和分配, 换 ...
- 5 分钟全面掌握 Python 装饰器
♚ 作者:吉星高照, 网易游戏资深开发工程师,主要工作方向为网易游戏 CDN 自动化平台的设计和开发,脑洞比较奇特,喜欢在各种非主流的领域研究制作各种不走寻常路的东西. ! Python的装饰器是面试 ...
- LeetCode——264. 丑数 II
编写一个程序,找出第 n 个丑数. 丑数就是只包含质因数 2, 3, 5 的正整数. 示例: 输入: n = 10 输出: 12 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 ...
- Servlet&JSP复习笔记 02
1.Servlet获取请求参数 获取请求参数依靠的是表单元素的name属性,广泛意义来说id属性是给客户端使用的,name属性是服务器使用的. a.获取Name-Value的方法: - getPara ...
- TPO5-3 The Cambrian Explosion
At one time, the animals present in these fossil beds were assigned to various modern animal groups, ...