状压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,先 ...
随机推荐
- 计蒜客 方程的解数(DFS)
问题描述 输出格式 输出一行,输出一个整数,表示方程的整数解的个数. 样例输入 - 样例输出 #include <stdio.h> #include <string.h> #i ...
- Windows Java桌面应用程序集成slf4j实现日志持久化
声明:迁移自本人CSDN博客https://blog.csdn.net/u013365635 Windows上一般的应用程序也可以通过日志系统打印日志到指定文件.通过这个例子想说明,问题处理的方法是多 ...
- 配对t检验
- ORM表之间高级设计
ORM表之间高级设计 一.表的继承 # db_test1 # 一.基表 # Model类的内部配置Meta类要设置abstract=True, # 这样的Model类就是用来作为基表 # 多表:Boo ...
- Huffman编码实验
一. 实验目的 熟练掌握哈夫曼树的建立和哈夫曼编码的算法实现. 二. 实验内容 根据哈夫曼编码的原理,编写一个程序,在用户输入结点权值的基础上求赫夫曼编码,并能把给定的编码进行译码. 三. 实验要求 ...
- Python 爬虫 爬取图片入门
爬虫 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动的抓取万维网信息的程序或者脚本. 用户看到的网页实质是由 HTML 代码构成的,爬 ...
- String--课后作业1
1.程序设计思想 输入一个字符串,利用charAt()提取指定位置的字符,判断该字符是否是x,y,z(大小写都一样),若是,则将该字符减23后强制转换为char型,若不是则将其加3后强制转换为char ...
- [原]C++新标准之std::chrono::duration
原 总结 C++11 chrono duration ratio 概览 std::chrono::duration 描述 类定义 duration_cast()分析 预定义的duration 示例代 ...
- matlab2016b
http://www.cnblogs.com/CQBZOIer-zyy/p/5933954.html
- Opencv笔记(二十)——直方图(二)
直方图均衡化 原理: 想象一下如果一副图像中的大多是像素点的像素值都集中在一个像素值范围之内会怎样呢?例如,如果一幅图片整体很亮,那所有的像素值应该都会很高.但是一副高质量的图像的像素值分布应该很广泛 ...