一个超级显然的状压
可惜我昨天没打
这题有个很简单的思维解法,想看戳我哦,比状压简单很多
言归正传,反正思维的解法我没想到,用状压草过去的,状压毕竟具有普适性
定 义 d p [ i ] [ j ] 为 构 造 好 第 i 个 c i 后 , 前 i 个 c 元 素 相 或 运 算 是 否 可 能 得 到 二 进 制 j 定义dp[i][j]为构造好第i个c_i后,前i个c元素相或运算是否可能得到二进制j 定义dp[i][j]为构造好第i个ci后,前i个c元素相或运算是否可能得到二进制j
这 个 j 的 范 围 就 是 [ 0 , 1 < < 9 ] 嘛 , 因 为 或 和 与 不 会 改 变 二 进 制 范 围 这个j的范围就是[0,1<<9]嘛,因为或和与不会改变二进制范围 这个j的范围就是[0,1<<9]嘛,因为或和与不会改变二进制范围
那 么 对 于 当 前 的 a i , 枚 举 一 个 b q 构 造 成 此 时 的 c i 那么对于当前的a_i,枚举一个b_q构造成此时的c_i 那么对于当前的ai,枚举一个bq构造成此时的ci
再 来 一 层 f o r 循 环 枚 举 与 运 算 完 c i 后 的 二 进 制 j 再来一层for循环枚举与运算完c_i后的二进制j 再来一层for循环枚举与运算完ci后的二进制j
再 来 一 层 f o r 循 环 枚 举 二 进 制 d 表 示 要 从 d p [ i − 1 ] [ d ] 转 移 而 来 再来一层for循环枚举二进制d表示要从dp[i-1][d]转移而来 再来一层for循环枚举二进制d表示要从dp[i−1][d]转移而来
于是转移方程是
d p [ i ] [ j ] = d p [ i ] [ j ] dp[i][j]=dp[i][j] dp[i][j]=dp[i][j] | d p [ i − 1 ] [ d ] dp[i-1][d] dp[i−1][d]
中间的竖线表示或运算
意 思 是 , 只 要 存 在 一 个 d 使 得 d p [ i − 1 ] [ d ] 可 行 , 那 么 d p [ i ] [ j ] 也 可 行 意思是,只要存在一个d使得dp[i-1][d]可行,那么dp[i][j]也可行 意思是,只要存在一个d使得dp[i−1][d]可行,那么dp[i][j]也可行
于是我们可以写下如下代码
dp[0][0]=1;
for(int i=1;i<=n;i++)
for(int q=1;q<=m;q++)
{
int s = (a[i]&b[q]) ;//当前构造的c[i]
for(int j=0;j<=(1<<9);j++)//本状态
{
if( (j&s)!=s ) continue;//本状态由于或上了c[i],二进制一定包含c[i]
for(int d=0;d<=j;d++)//上一状态d|s=j,所以不可能大于j
{
if( (d|s)!=j ) continue;
dp[i][j]|=dp[i-1][d];
}
}
}
拿起小拇指一算复杂度,哇呜呜
2 9 ∗ 2 9 ∗ 200 ∗ 200 = . . . . . . . . 2^9*2^9*200*200=........ 29∗29∗200∗200=........
但 是 仔 细 看 , 枚 举 d 的 那 个 循 环 , 很 多 工 作 是 不 需 要 的 但是仔细看,枚举d的那个循环,很多工作是不需要的 但是仔细看,枚举d的那个循环,很多工作是不需要的
现 在 已 知 s 和 j , 我 们 需 要 枚 举 的 都 是 s ∣ d = = j 的 那 些 d 现在已知s和j,我们需要枚举的都是s|d==j的那些d 现在已知s和j,我们需要枚举的都是s∣d==j的那些d
d或运算s变成j
或运算嘛,只有当d没有某一位的二进制而s有这位二进制才会使得d的这一位变成1
但 是 贡 献 给 d 哪 几 位 二 进 制 呢 ? 但是贡献给d哪几位二进制呢? 但是贡献给d哪几位二进制呢?
对于s的某一位是1的二进制有选和不选两种可能
d f s 爆 搜 出 所 有 情 况 , 存 入 v e c 数 组 中 即 可 dfs爆搜出所有情况,存入vec数组中即可 dfs爆搜出所有情况,存入vec数组中即可
那 么 现 在 v e c 存 的 是 s 贡 献 给 d 的 二 进 制 那么现在vec存的是s贡献给d的二进制 那么现在vec存的是s贡献给d的二进制
设 y 是 s 贡 献 给 d 的 二 进 制 数 , 那 么 d ∣ s = j 设y是s贡献给d的二进制数,那么d|s=j 设y是s贡献给d的二进制数,那么d∣s=j
解得 d = ( j 异 或 s ) d=(j异或s) d=(j异或s)
当然这貌似属于sos枚举子集的状压,你有兴趣可以去搜一下
#include
using namespace std;
int n,m,a[209],b[209];
int dp[209][1<<10];
vectorvec[209][209];
void dfs(int bit,int temp,int i,int j,int he)
{
if( bit==(1<<9) )
{
vec[i][j].push_back(he);
return;
}
if( bit&temp )
{
dfs(bit*2,temp,i,j,he);
dfs(bit*2,temp,i,j,he+bit);
}
else dfs(bit*2,temp,i,j,he);;
}
int main()
{
cin >> n >> m;
for(int i=1;i<=n;i++) cin >> a[i];
for(int j=1;j<=m;j++) cin >> b[j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int temp = (a[i]&b[j]);
dfs(1,temp,i,j,0);//爆搜
}
dp[0][0]=1;
for(int i=1;i<=n;i++)
for(int q=1;q<=m;q++)
{
int s = (a[i]&b[q]) ;
for(int j=0;j<=(1<<9);j++)//本状态
{
if( (j&s)!=s ) continue;
for(int d=0;d