1.普通状态压缩 DP

oi-wiki

I.P1896 [SCOI2005] 互不侵犯

\(f_{i,j,st}\) 表示前 \(i\) 行中放置了 \(j\) 个国王,当前行状态为 \(st\) 的方案数。可以预处理出合法的状态与其 popcount,转移时枚举当前行状态和上一行状态,合法就转移。

const int N = 20,inf = 1e9,mod = 998244353;
const ll inff = 1e18;
int n,k,f[N][110][1000];
vector<int> v; int pcnt[1000]; il void solve() {
//------------code------------
read(n,k);
rep(st,0,(1 << n) - 1) if (!((st << 1) & st) && !((st >> 1) & st))
v.pb(st),pcnt[st] = __builtin_popcount(st);
f[0][0][0] = 1;
rep(i,1,n) for (auto st : v) for (auto stt : v)
if (!(stt & st) && !((stt << 1) & st) && !((stt >> 1) & st)) {
rep1(j,k,pcnt[st]) f[i][j][st] += f[i - 1][j - pcnt[st]][stt];
}
int ans = 0;
for (auto st : v) ans += f[n][k][st];
write(ans,'\n');
// cerr << "Time : " << (db)(end - start) / CLOCKS_PER_SEC << " s" << endl;
return ;
}

II.P2704 [NOI2001] 炮兵阵地

考虑将一行某个位置能否部署状压成一个二进制数,然后判断状态合法时可以直接把它们按位与起来。\(f_{i,st,st'}\) 表示前 \(i\) 行中,当前行状态为 \(st\),上一行状态为 \(st'\),最多能摆放的炮兵部队的数量。压掉一维就能过。

const int N = 110,inf = 1e9,mod = 998244353;
const ull base = 131,base_ = 233;
const ll inff = 1e18;
int n,m,a[N];
int f[3][1034][1034];
vector<PII> v; il void solve() {
//------------code------------
read(n,m);
rep(i,1,n) {
string s; cin >> s;
rep(j,0,m - 1) a[i] += (s[j] == 'H') * (1 << (m - 1 - j));
}
rep(st,0,(1 << m) - 1) if (!(st & (st >> 1)) && !(st & (st >> 2))) v.pb(st,__builtin_popcount(st));
memset(f,-0x3f,sizeof f);
for (auto x : v) f[1][x.fst][0] = x.snd;
rep(i,2,n)
for (auto st : v) if (!(st.fst & a[i]))
for (auto stt : v) if (!(st.fst & stt.fst) && !(stt.fst & a[i - 1]))
for (auto sttt : v) if (!(st.fst & sttt.fst) && !(stt.fst & sttt.fst) && !(sttt.fst & a[i - 2]))
chmax(f[i % 3][st.fst][stt.fst],f[(i - 1) % 3][stt.fst][sttt.fst] + st.snd);
int ret = 0;
for (auto st : v) if (!(st.fst & a[n]))
for (auto stt : v) if (!(st.fst & stt.fst) && !(stt.fst & a[n - 1]))
chmax(ret,f[n % 3][st.fst][stt.fst]);
write(ret,'\n');
return ;
}

III.P1879 [USACO06NOV] Corn Fields G

板子。\(f_{i,st}\) 表示前 \(i\) 行中当前行状态为 \(st\) 的种植方案数。

const int N = 22,inf = 1e9,mod = 1e8;
const ull base = 131,base_ = 233;
const ll inff = 1e18;
int n,m,a[N]; ll f[N][4100];
vector<int> v; il void solve() {
//------------code------------
read(n,m);
rep(i,1,n)
rep(j,1,m) {
int x; read(x);
a[i] += (1 << (m - j)) * (!x);
}
rep(st,0,(1 << m) - 1) if (!(st & (st << 1))) v.pb(st);
f[0][0] = 1;
rep(i,1,n)
for (auto st : v) if (!(st & a[i]))
for (auto stt : v) if (!(st & stt) && !(stt & a[i - 1]))
f[i][st] = (f[i][st] + f[i - 1][stt]) % mod;
ll ans = 0;
for (auto st : v) ans = (ans + f[n][st]) % mod;
write(ans,'\n');
return ;
}

IV.P3052 [USACO12MAR] Cows in a Skyscraper G

\(f_{st}\) 表示当前已将哪些物品进行分组,最小的分组数量。维护一个当前 \(f_{st}\) 的最大剩余体积,每次能放下就放,放不下就增加组数,同时更新最大剩余体积。

const int N = 3e5 + 10,inf = 1e9,mod = 998244353;
const ull base = 131,base_ = 233;
const ll inff = 1e18;
int n,m,w[20]; PII f[N]; il void solve() {
//------------code------------
read(n,m);
rep(i,1,n) read(w[i]);
rep(st,1,(1 << n) - 1) f[st] = {-inf,inf};
f[0] = {m,1};
rep(st,1,(1 << n) - 1)
rep(i,0,n - 1) if (st >> i & 1) {
if (f[st].snd >= f[st ^ (1 << i)].snd && f[st ^ (1 << i)].fst >= w[i + 1]) {
chmax(f[st].fst,f[st ^ (1 << i)].fst - w[i + 1]);
f[st].snd = f[st ^ (1 << i)].snd;
} else if (f[st].snd >= f[st ^ (1 << i)].snd + 1 && f[st ^ (1 << i)].fst < w[i + 1]) {
chmax(f[st].fst,m - w[i + 1]);
f[st].snd = f[st ^ (1 << i)].snd + 1;
}
// cerr << st << " " << i << " " << f[st].snd << '\n';
}
write(f[(1 << n) - 1].snd,'\n');
return ;
}

V.P2396 yyy loves Maths VII

显然有 \(\mathcal{O(2^n n)}\) 的做法,但是 1 s 可能跑不过,考虑优化 \(n\) 的那一维,每次取出状态的 lowbit 来进行转移,并减掉它,这样可以减少大量无用的判断,能过此题。最慢的点跑了有 600 ms

const int N = 2e7 + 10,inf = 1e9,mod = 1e9 + 7;
const ull base = 131,base_ = 233;
const ll inff = 1e18;
int n,m,a[N],b[3],f[N],g[N]; il int lowbit(int x) { return x & -x; } il void solve() {
//------------code------------
read(n); rep(i,1,n) read(a[(1 << i - 1)]);
read(m); rep(i,1,m) read(b[i]);
f[0] = 1;
rep(st,1,(1 << n) - 1) {
g[st] = g[st ^ lowbit(st)] + a[lowbit(st)];
bool fl = 1;
rep(i,1,m) if (g[st] == b[i]) { fl = 0; break; }
if (!fl) continue;
int x = st;
for (; x; x -= x & -x) f[st] = (f[st] + f[st ^ lowbit(x)]) % mod;
}
write(f[(1 << n) - 1],'\n');
return ;
}

VI.P2831 [NOIP2016 提高组] 愤怒的小鸟

可以发现两个点可以唯一确定一条抛物线,预处理 \(sta_{i,j}\) 表示第 \(i\) 个点和第 \(j\) 个点组成的抛物线经过的点,对其做状压处理。现在就有一个 \(\mathcal{O(T2^n n^2)}\) 的做法,对于当前状态枚举接下来打哪条抛物线。这样并不能通过所有测试点,考虑优化。发现最终需要的答案是 \(f_{2^n - 1}\),其余有 \(f\) 对最终答案并没有贡献,那么它是无用的,显然我们最终状态 \(2^n - 1\) 在二进制下是有 \(n\) 个 1 的,那么每次对于当前状态要推到最终状态,二进制下第一个为 0 的位置到最后一定会被转移掉,变为 1,那么我们就可以找到这个位置,再枚举它和其它的点的抛物线,用此抛物线的 \(sta\) 来转移就足矣。最后时间复杂度是 \(\mathcal{O(T2^n n)}\) 的。

const int N = 270010,inf = 1e9,mod = 998244353;
const ull base = 131,base_ = 233;
const db eps = 1e-8;
const ll inff = 1e18;
int n,m; pair<db,db> a[N];
int cnt[20][20],f[N]; il void solve() {
//------------code------------
read(n,m);
rep(i,1,n) cin >> a[i].fst >> a[i].snd;
rep(i,1,n) rep(j,i + 1,n) {
db x = a[i].fst,y = a[i].snd;
db xx = a[j].fst,yy = a[j].snd;
if (fabs(x - xx) < eps) { cnt[i][j] = cnt[j][i] = 0; continue; }
db A = (y / x - yy / xx) / (x - xx),B = y / x - x * A;
// cerr << i << " " << j << " " << A << " " << B << '\n';
if (A >= 0) { cnt[i][j] = cnt[j][i] = 0; continue; }
cnt[i][j] = 0;
rep(k,1,n) if (fabs(A * a[k].fst * a[k].fst + B * a[k].fst - a[k].snd) < eps)
cnt[i][j] += (1 << k - 1);
cnt[j][i] = cnt[i][j];
}
rep(st,0,(1 << n) - 1) f[st] = inf;
f[0] = 0;
rep(st,0,(1 << n) - 2) {
int p = -1;
rep(i,0,n - 1) if (!(st >> i & 1)) { p = i; break; }
chmin(f[st | (1 << p)],f[st] + 1);
rep(i,1,n) if (i != p && ~cnt[p + 1][i]) chmin(f[st | cnt[p + 1][i]],f[st] + 1);
}
write(f[(1 << n) - 1],'\n');
return ;
}

VII.P2473 [SCOI2008] 奖励关

显然有 \(f_{i,st}\) 表示前 \(i\) 次中,吃过的宝物的集合为 \(st\) 的最大期望分值。然后你会发现对于 \(f_i\),有一些状态是明显不合法的,此题起点唯一,但终点不唯一且有一些状态不可达的情况,所以需要倒推。

const int N = 20,inf = 1e9,mod = 998244353;
const ull base = 131,base_ = 233;
const ll inff = 1e18;
int k,n,s[N],p[N];
db f[110][32770]; il void solve() {
//------------code------------
read(k,n);
rep(i,1,n) {
read(p[i]); s[i] = 0;
int x; read(x);
while (x) {
s[i] |= (1 << x - 1);
read(x);
}
}
f[k + 1][(1 << n) - 1] = 0;
rep1(i,k,1)
rep(st,0,(1 << n) - 1) {
// cerr << i << " " << st << '\n';
rep(j,1,n)
if ((st & s[j]) == s[j]) f[i][st] += max((f[i + 1][st | (1 << j - 1)] + p[j] * 1.0),f[i + 1][st]) / (db)n;
else f[i][st] += f[i + 1][st] / (db)n;
}
db ans = f[1][0];
printf("%.6lf\n",ans);
return ;
}

2.插头 DP

插头 DP 用于解决需要维护状态连通性的状压 DP 问题。

oi-wiki

I.「HDU 1400」Mondriaan’s Dream

\(f_{i,j,s}\) 表示当前转移到了 \((i,j)\),轮廓线上的状态为 \(s\) 的合法覆盖方案数。注意状态是按照 \(j\) 的大小从小到大依次存储在二进制位下的。

可以进行分类讨论:

  1. 当状态 \(s\) 的第 \(j - 1\) 位上为 1 而第 \(j\) 位上为 0,即左边有插头过来而上面没有,那么就是横放,下一个状态的第 \(j - 1\) 位应当为 0
  2. 当状态 \(s\) 的第 \(j\) 位上为 1 而第 \(j - 1\) 位上为 0,即上面有插头过来而左边没有,那么就是竖放,下一个状态的第 \(j\) 位应当为 0
  3. 当第 \(j\) 和 \(j - 1\) 位上均为 0 时,即可竖放也可横放,结合以上两种转移即可;
  4. 均为 1 时不合法。
const int N = 15,inf = 1e9,mod = 998244353;
const ull base = 131,base_ = 233;
const ll inff = 1e18;
int n,m,f[N][N][1 << N]; il void solve() {
//------------code------------
while (1 + 1 == 2) {
read(n,m);
if (!n && !m) return ;
rep(i,0,n) rep(j,0,m) rep(s,0,(1 << m + 1) - 1) f[i][j][s] = 0;
f[1][0][0] = 1;
rep(i,1,n) {
rep(j,1,m) {
rep(s,0,(1 << m + 1) - 1) {
if (!(s >> (j - 1) & 1)) f[i][j][s ^ (1 << j)] += f[i][j - 1][s];
if (!(s >> j & 1)) f[i][j][s ^ (1 << j - 1)] += f[i][j - 1][s];
}
}
rep(s,0,(1 << m + 1) - 1) if (!(s >> m)) f[i + 1][0][s << 1] = f[i][m][s];
}
write(f[n + 1][0][0],'\n');
}
return ;
}

「UVA 11270」Tiling Dominoes

双倍经验。同上。

等待更新。。

状压 DP 做题记录的更多相关文章

  1. 状压dp做题笔记

    CodeChef Factorial to Square (分块决策) Description 给定一个n,要求在[1,n]中删除一些数,并使剩下的数的乘积是一个完全平方数,同时要求乘积最大,求删除方 ...

  2. 【bzoj3195】【 [Jxoi2012]奇怪的道路】另类压缩的状压dp好题

    (上不了p站我要死了) 啊啊,其实想清楚了还是挺简单的. Description 小宇从历史书上了解到一个古老的文明.这个文明在各个方面高度发达,交通方面也不例外.考古学家已经知道,这个文明在全盛时期 ...

  3. 7月15日考试 题解(链表+状压DP+思维题)

    前言:蒟蒻太弱了,全打的暴力QAQ. --------------------- T1 小Z的求和 题目大意:求$\sum\limits_{i=1}^n \sum\limits_{j=i}^n kth ...

  4. 【bzoj1087】【互不侵犯King】状压dp裸题(浅尝ACM-D)

    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=54329606 向大(hei)佬(e)势力学(di ...

  5. POJ 3254 - Corn Fields - [状压DP水题]

    题目链接:http://poj.org/problem?id=3254 Time Limit: 2000MS Memory Limit: 65536K Description Farmer John ...

  6. hdu 1185 状压dp 好题 (当前状态与上两行有关系)

    /* 状压dp 刚开始&写成&&看了好长时间T0T. 状态转移方程 dp[i][k][j]=Max(dp[i][k][j],dp[i-1][l][k]+num[i][j]);( ...

  7. poj 3254 状压dp入门题

    1.poj 3254  Corn Fields    状态压缩dp入门题 2.总结:二进制实在巧妙,以前从来没想过可以这样用. 题意:n行m列,1表示肥沃,0表示贫瘠,把牛放在肥沃处,要求所有牛不能相 ...

  8. DP 做题记录 II.

    里面会有一些数据结构优化 DP 的题目(如 XI.),以及普通 DP. *I. P3643 [APIO2016]划艇 题意简述:给出序列 \(a_i,b_i\),求出有多少序列 \(c_i\) 满足 ...

  9. HihoCoder - 1048 状压DP 经典题

    hihocoder题解说的十分清晰了,这份代码就是从讲解里学习的 方案数就是不断枚举合法状态下横放竖放或两者均可 合法判断的依据是记录当前行和下一行的状态 防止重复枚举的方法是先按行后按列 递归基瞎写 ...

  10. 二维状压DP经典题

    炮兵阵地 题目链接 题目大意:在n*m的地图上放置炮兵,每个炮兵的攻击范围是上下左右两格内,有两种不同的地形,山地(用"H" 表示),平原(用"P"表示),只有 ...

随机推荐

  1. redis的主从复制master/slaver

    什么是Redis的复制 ​ 就是我们常说的主从复制,主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主. 复制原理 Slave启动成 ...

  2. 数据库系统原理——第三章 关系数据库标准语言SQL

    @ 目录 1.SQL的特点 2.SQL的组成 3SQL语句 3.1数据库的基本操作 3.2 基本表的定义.修改.删除 3.3索引的建立与删除 3.4数据更新 3.5数据查询 3.5.1单表查询 3.5 ...

  3. mysql文本数据导入

    MySQL使用mysqlimport命令导入文本文件 MySQL中,可以使用mysqlimport命令将文本文件导入到MySQL数据库中.基本的语法格式如下: mysqlimport <dbna ...

  4. 人形机器人是未来?6只手臂加AI模型,异形机器人重塑种植业。

    图源:reddit user IlustriousTea 近日,一则视频在媒体上引起了人们的讨论.国外一处苹果园里,机械嗡鸣声中,六只机械手熟练且快速地采摘成熟的苹果. 这是2018年于美国加利福尼亚 ...

  5. Python311新特性-特化指令specializing adaptive interpreter-typing-asyncio

    Python3新特性 python3.11增加了许多特性,让python更快更加安全,本文从应用层面来讲一下python3.11的这些新特性 特化自适应解析器是什么,如何利用特化写出更高性能的代码 如 ...

  6. 计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议

    文章目录 一.TCP/IP五层模型(重要) 二.应用层常见的协议 三.TCP与UDP 3.1 TCP.UDP的区别(重要) 3.2 运行于TCP.UDP上的协议 3.3 TCP的三次握手.四次挥手 3 ...

  7. 2.搭建K8S集群前置知识

    搭建K8S集群 搭建k8s环境平台规划 单master集群 单个master节点,然后管理多个node节点 多master集群 多个master节点,管理多个node节点,同时中间多了一个负载均衡的过 ...

  8. JAVA MemCache 史无前例的详细讲解!看完包精通MEMCACHE!

    Memcach什么是Memcache Memcache集群环境下缓存解决方案 Memcache是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式 ...

  9. 简单理解Linux File的操作

    类Unix系统是支持多个进程打开同一个文件,进行读写. 得益于类Unix系统对于文件操作的特殊设计. 分为三个数据结构 进程表项:其中包含进程中打开的文件和设备的文件描述符.还包含该文件描述符对应的文 ...

  10. 新型大语言模型的预训练与后训练范式,谷歌的Gemma 2语言模型

    前言:大型语言模型(LLMs)的发展历程可以说是非常长,从早期的GPT模型一路走到了今天这些复杂的.公开权重的大型语言模型.最初,LLM的训练过程只关注预训练,但后来逐步扩展到了包括预训练和后训练在内 ...