状压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,先 ...
随机推荐
- SDL实践:产品经理如何驱动产品安全建设
一.序言 本文从产品经理的角度出发,对产品经理的安全职责.产品驱动安全的内涵.工作内容.工作方法.所需安全资源.以及产品经理的安全工作量进行了分析.希望所有产品经理在没有心理负担的情况下,有目标.有方 ...
- 使用图数据库 Nebula Graph 数据导入快速体验知识图谱
本文由 Nebula Graph 实习生@王杰贡献. 最近 @Yener 开源了史上最大规模的中文知识图谱——OwnThink(链接:https://github.com/ownthink/Knowl ...
- nouveau :failed to create kernel chanel,-22
一:錯誤描述:今天在重啓 Ubuntu 的過程中,出現下圖的 grub 選項,系統重啓/開機之後出現以下畫面,然後選擇 Ubuntu 之後黑屏,提示錯誤:nouveau :failed to crea ...
- Java之解决线程安全问题的方式三:Lock锁
import java.util.concurrent.locks.ReentrantLock; /** * 解决线程安全问题的方式三:Lock锁 --- JDK5.0新增 * * 1. 面试题:sy ...
- Codeforces 1294B - Collecting Packages
题目大意: 机器人从(0,0)开始,他只能往上'U'或者往右'R'走 坐标系中有着很多包裹,分别在一些点上 机器人需要走过去把这些包裹全部收集起来 问能不能做到 如果能,再输出移动方式,相同移动方式输 ...
- 洛谷 P5018 对称二叉树
题目传送门 解题思路: 先计算每个点的子树有多少节点,然后判断每个子树是不是对称的,更新答案. AC代码: #include<iostream> #include<cstdio> ...
- js等于符号的详解
JavaScript == 与 === 区别 1.对于 string.number 等基础类型,== 和 === 是有区别的 a)不同类型间比较,== 之比较 "转化成同一类型后的值&quo ...
- 题解-------CF235B Let's Play Osu!
传送门 题目大意 求出总得分的期望值. 思路 还没有学习数学期望的小朋友赶紧去学一下数学期望,这里只提供公式: $E\left ( x \right )=\sum_{k=1}^{\infty }x_{ ...
- 吴裕雄--天生自然 pythonTensorFlow图形数据处理:windows操作系统安装指定版本的tensorflow
pip install tensorflow==1.14.0
- LeetCode No.157,158,159
No.157 Read 用 Read4 读取 N 个字符 题目 给你一个文件,并且该文件只能通过给定的 read4 方法来读取,请实现一个方法使其能够读取 n 个字符. read4 方法: API r ...