目录

一.普通型

蒙德里安的梦想

题意:

思路:

code:

#2153. 「SCOI2005」互不侵犯

题意:

思路:

code:

P1879 [USACO06NOV]Corn Fields G

题意:

思路:

code:

P2704 [NOI2001] 炮兵阵地

题意:

思路:

code:

Most Powerful

题意:

思路:

code:

方格取数(1)

题意:

思路:

code:

中国象棋

题意:

思路:

code:

[JXOI2012]奇怪的道路

题意:

思路:

code:

Victor and World (旅行商问题/TSP问题/最短路径问题 ) ()Floyd + 状压dp)

题意:

思路:

code:

P2915 [USACO08NOV]Mixed Up Cows G

题意:

思路:

code:

最短Hamilton路径

题意:

思路:

code:

P3052 [USACO12MAR]Cows in a Skyscraper G

题意:

思路:

code:

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

题意:

思路:

code:

P3959 [NOIP2017 提高组] 宝藏

题意:

思路:

code:

AC Challenge2018 南京网络赛 E

题意:

思路:

code:

二.高维前缀和 / 动态高维前缀和

Or Plus Max

题意:

思路:

code:

Bits And Pieces

题意:

思路:

code:

Jzzhu and Numbers

题意:

思路:

code:

Vowels

题意:

思路:

code:

Compatible Numbers

题意:

思路:

code:

三.轮廓线型 / 插头dp


状压dp注意事项:

注意数据范围一般都得long long

^删除, |添加, 看某位是否在状态中存在(1 << j) & S

一.普通型

蒙德里安的梦想

题意:

给你n*m的棋盘, 每次你可以放进去2*1的小长方形, 问你最多能够放多少个.

思路:

(蓝书上是按行枚举, AcWing上是按列枚举, 这里我采用按列枚举的方法.)

整体思路:摆放方块的时候,先放横着的,再放竖着的, 下面按照这个思路来走.

首先我们用表示前列的状态为时能放的最大值.我们一列一列的来看.

我们规定对第列, 的某个状态, 其对应在棋盘中的位置如果为是横着的长方形的左半部分, 那么它的值为1, 否则为0.

下面我们来构造状态转移方程. 显然是: 即从前一列的合法情况转移而来.

下面我们考虑如何才能合法的转移呢? 想要从前一个状态转移到后一个状态有两个关键点:

1)若想要将列的状态转移到第列的状态不能同时为1, 因为要1代表的是长方形的前一半.不能两个都是前一半! 表示为 (j & k) == 0. 消除了对应位置同时为1的可能.

2)每一列连续的空着的长度必须是偶数. 是为了能够摆放竖着的, 呼应我们的整体思路.先横后竖 因此若能够转移, 新的状态中一定也不存在上述非法情况; 即. 采用或的原因如下:

j | k 表示的是只要前后状态对应位置有一位为1,连续0就会被隔断,所以,更新后的状态中的对应位置实际的是j | k. 若j | k合法表示原来的和现在的都是合法的.

由此我们得到了进行状态转移的前提: (j & k) == 0 && in_s[j | k]

PS:1.关于处理2)的情况我们需要预处理出所有合法的(对每一列进行预处理). 2.需要设置, 其他全部为0. 因为最小的情况n=1, m=1.可以放下一个.

code:

#include <bits/stdc++.h>using namespace std;
typedef long long ll;
ll in_s[1<<11];
ll f[12][1<<11];//按列枚举void solve() {int n, m;while (cin >> n >> m, n || m) {//预处理没有奇数个连续0的情况for (int i = 0; i < (1 << 11); i++ ) {int cnt = 0; // 统计连续0in_s[i] = 1;// 默认没有奇数个0//遍历i的m位for (int j = 0; j < n; j++) {//当前位是奇数if ((i >> j) & 1)  {//偶数的数量是奇数个 -> 不合法if (cnt & 1) {in_s[i] = 0;break;}}else //当前位是偶数cnt ++;}if (cnt & 1)in_s[i] = 0;}f[0][0] = 1;for (int i = 1; i <= m; i++) {for (int j = 0; j < (1 << n); j++) {f[i][j] = 0;for (int k = 0; k < (1 << n); k++) {if ((j & k) == 0 && in_s[j | k]) {f[i][j] += f[i - 1][k];}}}}cout << f[m][0] << endl;}
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int t = 1;while (t--) {solve();}return 0;
}

#2153. 「SCOI2005」互不侵犯

题意:

n*n的格子, 让你放k个国王, 问你可行的方案有多少种? 国王会攻击其上下左右 左上 左下 右上 右下等八个方向的其他人.

思路:

笑死 第一次做根本没思路

我们按行枚举  表示第i行, 状态为j 时, 已经放了k个国王的方案数量.

大概流程就是1)预处理每行合法状态 2)设置第一行的情况全为1 3)循环更新 4)循环累加答案

状压dp最大的问题就是位运算了, 比如   (首先注意二进制从右向左看) 从右向左若1则放0则不放, 由此我们把一行中的一个状态压缩成了一个二进制串, 我们想要方便的访问, 还要把他转化成十进制存在一个数组中方便之后对其访问, 转化成十进制是. 因此我们存储方式为.

对于本题, 存储状态用一个数组, 存储每行中放多少国王用另一个. 并且在实现时候还要注意题目中给的国王间互相排斥的条件, 这个也要通过位运算和与来实现. 转化成二进制画画图能好理解一些.

具体实现不展开来说了, 写在代码里把.

code:

#include <bits/stdc++.h>using namespace std;
typedef long long ll;ll n, goal, cnt;
ll f[250][250][250];
ll sta[250], num[250];void pre() {for (int i = 0; i < (1 << n); i++) {//往左移动, 如果不等于0代表有相邻的国王 不合法if (i & (i << 1))continue;ll sum = 0;for (int j = 0; j < n; j++) if (i & (1 << j)) //统计i的二进制下多少位是1sum ++;sta[++cnt] = i; //记录可用状态的十进制num[cnt] = sum; //记录此状态国王数量}
}void solve() {cin >> n >> goal;pre();for (int i = 1; i <= cnt; i++)f[1][i][num[i]] = 1;for (int i = 2; i <= n; i++) {for (int j = 1; j <= cnt; j++) { // 第i行 状态for (int k = 1; k <= cnt; k++) { // 第i - 1 行 状态if (sta[j] & sta[k]) //上下重复continue;if ((sta[j] << 1) & sta[k])//左上右下重复continue;if (sta[j] & (sta[k] << 1))//右上左下重复continue;for (int s = goal; s >= num[j]; s--)f[i][j][s] += f[i-1][k][s - num[j]];}}}ll res = 0;for (int i = 1; i <= cnt; i++)res += f[n][i][goal];cout << res << endl;
}int main() {solve();return 0;
}

P1879 [USACO06NOV]Corn Fields G

题意:

0, 1 矩阵 给定你条件(见原题), 让你给出最大方案数量

思路:

dp[i][j][k]表示的是前i行状态为j时候的方案数量.

1)预处理状态和数量

2)1.不会选择相邻的 2. 贫瘠的不能种草

3)注意先枚举第i行, 后枚举i-1

code:

#include <bits/stdc++.h>using namespace std;
const int MOD = 1e9;int f[13][5000], sta[5000], num[13];
int a[13][13];
int n, m;void pre() {//模拟草地情况的二进制表述for (int i = 1; i <= m; i++)for (int j = 1; j <= n; j++)num[i] = (num[i] << 1) + a[i][j];//求出状态for (int i = 0; i < (1 << n); i++)sta[i] = ( (i & (i << 1)) == 0) && ( (i & (i >> 1)) == 0);
}void solve() {cin >> m >> n;for (int i = 1; i <= m; i++)for (int j = 1; j <= n; j++)cin >> a[i][j];pre();f[0][0] = 1;for (int i = 1; i <= m; i++)for (int j = 0; j < (1 << n); j++)if (sta[j] && ((j & num[i]) == j)) //状态是合法的,且不会在贫瘠的草地上for (int k = 0; k < (1 << n); k++) //上下两行之间没有相邻的草地if ((k & j) == 0)f[i][j] = (f[i][j] + f[i - 1][k]) % MOD;int res = 0;for (int i = 0; i < (1 << n); i++)res = (res + f[m][i]) % MOD;cout << res << endl;
}signed main() {ios::sync_with_stdio(false);cin.tie(nullptr);solve();return 0;
}

P2704 [NOI2001] 炮兵阵地

题意:

见原题.

思路:

还是按行枚举, 判断前两行有没有炮兵, 判断是不是山丘, 判断左右两列有没有炮兵, 不容易想到正解, 很复杂实现起来.

f[i][k][k] 表示第i行状态为j, 上一行状态为k的情况数. 挺恶心的...

code:

#include <bits/stdc++.h>using namespace std;
typedef long long ll;
ll n, m, f[105][1005][1005], a[105];
char c;int get(int x) { //二进制下int sum = 0;while (x)sum += x % 2, x >>= 1;return sum;
}bool check(int x) {return (x & (x << 1)) | (x & (x << 2)) | (x & (x >> 1)) | (x & (x >> 2));
}void solve() {cin >> n >> m;for (int i = 2; i <= n + 1; ++i)for (int j = 1; j <= m; ++j)cin >> c, a[i] = (a[i] << 1) + (c == 'H');ll MAX = (1 << m) - 1;for (int i = 2; i <= n + 1; ++i) {for (int j = 0; j <= MAX; ++j) {if (j & a[i - 2] || check(j))continue;for (int k = 0; k <= MAX; ++k) {if ((k & a[i - 1]) || (j & k) || check(k))continue;for (int l = 0; l <= MAX; ++l) {if ((l & a[i]) || (l & j) || (l & k) || check(l))continue;f[i][k][l] = max(f[i][k][l], f[i - 1][j][k] + get(l));}}}}ll res = 0;for (int k = 0; k <= MAX; k++) {if (k & a[n])continue;for (int l = 0; l <= MAX; l++)res = max(res, f[n + 1][k][l]);}cout << res << endl;
}signed main() {ios::sync_with_stdio(false);cin.tie(nullptr);solve();return 0;
}

Most Powerful

题意:

给你n个原子,两两相撞其中一个消失, 产生一定的能量,给出任意两原子相撞能产生的能量,求可能产生的最大能量?

思路:

dp[]表示当前状态下能够达到的最大值是多少

code:

#include <bits/stdc++.h>using namespace std;int power[11][11];
int dp[1 << 11];void solve() {int n, res = 0;while (cin >> n && n != 0) {for (int i = 0; i < n; ++i) {for (int j = 0; j < n; ++j) {cin >> power[i][j];}}int MAX = (1 << n) - 1;memset(dp, 0, sizeof dp);for (int s = 0; s <= MAX; ++s) {for (int i = 0; i < n; ++i) {if (((1 << i) & s) == 0)for (int j = 0; j < n; ++j) {if (((1 << j) & s) == 0) {if (i != j) {dp[s | (1 << i)] = max(dp[s | (1 << i)], dp[s] + power[j][i]);}}}}}res = 0;for (int s = 0; s <= MAX; ++s)res = max(res, dp[s]);cout << res << endl;}
}signed main() {ios::sync_with_stdio(false);cin.tie(nullptr);solve();return 0;
}

方格取数(1)

题意:

板子题

思路:

code:

#include<bits/stdc++.h>using namespace std;int dp[25][20000];
int tot[20000];
int a[25][25];
int get_num(int i,int x) {int t=1;int sum=0;while(x){if(x&1){sum+=a[i][t];}x/=2;t++;}return sum;
}
int main(){int n;while(cin >> n){memset(dp,0,sizeof(dp));for(int i=1; i<=n; i++){for(int j=1; j<=n; j++){scanf("%d",&a[i][j]);}}int cut=0;for(int i=0; i<(1<<n); i++){if((i&(i>>1))==0){tot[++cut]=i;}}for(int i=1; i<=n; i++){for(int j=1; j<=cut; j++){int va=get_num(i,tot[j]);for(int k=1; k<=cut; k++){if((tot[j]&tot[k])==0)dp[i][j]=max(dp[i][j],dp[i-1][k]+va);}}}int ma=0;for(int i=1; i<=cut; i++){ma=max(ma,dp[n][i]);}printf("%d\n",ma);}return 0;
}

中国象棋

题意:

见原题

思路:

学习的大佬的思路

code:

#include <bits/stdc++.h>
#define int long long
#define MOD 9999973using namespace std;
int n, m, res;int f[111][111][111];int C(int x) {return ((x * (x - 1)) / 2) % MOD;
}
signed main() {cin >> n >> m;f[0][0][0] = 1;for (int i = 1; i <= n; i++) {for (int j = 0; j <= m; j++) {for (int k = 0; k <= m - j; k++) {f[i][j][k] = f[i - 1][j][k];if (k >= 1)(f[i][j][k] += f[i - 1][j + 1][k - 1] * (j + 1));if (k >= 1)(f[i][j][k] += f[i - 1][j][k - 1] * j * (m - j - k + 1));if (k >= 2)(f[i][j][k] += f[i - 1][j + 2][k - 2] * (((j + 2) * (j + 1)) / 2));if (j >= 1)(f[i][j][k] += f[i - 1][j - 1][k] * (m - j - k + 1));if (j >= 2)(f[i][j][k] += f[i - 1][j - 2][k] * C(m - j - k + 2));f[i][j][k] %= MOD;}}}for (int i = 0; i <= m; i++)for (int j = 0; j <= m; j++)(res += f[n][i][j]) %= MOD;cout << (res + MOD) % MOD << endl;return 0;
}

[JXOI2012]奇怪的道路

题意:

思路:

待补? 异或操作很奇怪...

code:

#include <bits/stdc++.h>
const int MOD = 1e9+7;
using namespace std;int n, m, k;
int f[44][44][555][11];signed main() {cin >> n >> m >> k;f[2][0][0][0]=1;for (int i=2;i<=n;i++)for (int j=0;j<=m;j++)for (int s=0;s<(1<<(k+1));s++) {for (int l=0;l<k;l++){f[i][j][s][l+1]+=f[i][j][s][l];f[i][j][s][l+1]%=MOD;if(i-k+l>0&&j<m)f[i][j+1][s^(1<<k)^(1<<l)][l]+=f[i][j][s][l],f[i][j+1][s^(1<<k)^(1<<l)][l]%=MOD;}if(!(s&1)) {f[i+1][j][s>>1][0]+=f[i][j][s][k];f[i+1][j][s>>1][0]%=MOD;}}cout << f[n + 1][m][0][0] << endl;return 0;
}

Victor and World (旅行商问题/TSP问题/最短路径问题 ) ()Floyd + 状压dp)

题意:

有n个点, m条边, 从一个点到另一个点有一个花费w. 问你从1号点开始遍历所有的点最后回到1号点的最小花费是多少?

思路:

本题采用Floyd + 状压dp

1)首先用Floyd处理两点之间的最小权值

2)进行状压dp, 我们规定表示, 在i节点, 状态为j时的最小权值和, 枚举合法状态, 并且注意当前状态是由前一个状态转移而来即f[i][S] = min(f[i][S], f[j][S ^ (1 << i)] + g[j][i] .

3)遍历除了原点外的所有点, 找到那个到原点权值和最小的, 并替代

tips:

1)u, v我改成从0开始的了

2)记得初始化 f,和g

3)f[j][S ^ (1 << i)] 表示的是把i节点从当前状态中拿走, 如果是 | 的话相当于加上来

4)为什么最后要找最小的而不是直接输出f[0][(1 << n) - 1], 而要用f[i][(1 << n) - 1] + g[i][0]来和其取min呢? 因为最后访问的点不会是起点. 枚举最后访问的点是哪个(遍历完所有的了已经用(1<<n)-1表示状态, 因此我们枚举最后一个点到原点的距离的最小值方可得到正确答案!

code:

#include <bits/stdc++.h>
using namespace std;int f[25][1<<17]; //f[i][j] 表示当前在i节点, 当前状态j的最小权值之和
int g[25][25];
int n, m;void solve() {memset(g, 0x3f, sizeof g);cin >> n >> m;while (m--) {int u, v, w;cin >> u >> v >> w;u--, v--; //改成从0开始g[u][v] = g[v][u] = min(g[u][v], w);}//Floydfor(int k = 0; k < n; k++) // for(int i = 0; i < n; i++)for(int j = 0; j < n; j++) g[i][j] = min(g[i][j], g[i][k] + g[k][j]);//状压dpmemset(f,0x3f,sizeof(f));f[0][1] = 0;for (int S = 1; S < (1 << n); S++) //枚举状态for (int i = 0; i < n; i++) if (S & (1 << i)) //先枚举目标节点for (int j = 0; j < n; j++) //枚举出发节点if (S & (1 << j) && g[j][i])f[i][S] = min(f[i][S], f[j][S ^ (1 << i)] + g[j][i]);//找到最小的for(int i = 1; i < n; i++) f[0][(1 << n) - 1] = min(f[0][(1 << n) - 1], f[i][(1 << n) - 1] + g[i][0]);cout << f[0][(1 << n) - 1] << "\n";}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int t;cin >> t;while (t--) {solve();}return 0;
}

P2915 [USACO08NOV]Mixed Up Cows G

题意:

重排序列, 相邻的差必须大于k.

思路:

根据数据范围可知一定是状压dp. 设计dp的时候想到一定有一个维度是表示状态的.另一个想到是编号. 那我们由无后效性联想到, 可以设计成f[i][j] i表示当前以i为结尾, 状态为j时候的方案数量.由此我们步骤基本明朗.

1)初始化每个点, 只有自己且是队尾的f[i][(1<<n)]应该设置成1, 因为这只有一种情况

2)for循环枚举状态, 枚举起点, 终点, 记得既要满足该点在状态中存在, 也要满足题目中的距离差. 接下来有两种做法j->k and k->j, 见代码.

3)把每个点做队尾且满状态的情况累加得到答案.

ps:似乎^的做法跑得更快?

code:

#include <bits/stdc++.h>using namespace std;int a[25];
long long f[25][1 << 17];signed main() {ios::sync_with_stdio(false);cin.tie(0);int n, m;cin >> n >> m;for (int i = 0; i < n; i++) {cin >> a[i];}for (int i = 0; i < n; i++) {f[i][(1 << i)] = 1;}for (int i = 0; i < (1 << n); i++) {for (int j = 0; j < n; j++) {if (i & (1 << j)) {for (int k = 0; k < n; k++) {//1) 从k到j加上 k是起点if (i & (1 << k) && abs(a[j] - a[k]) > m) {f[j][i] += f[k][i ^ (1 << j)];}//2) 从j到k加上 j是起点/*if (!(i & (1 << k)) && abs(a[j] - a[k]) > m) {f[k][i | (1 << k)] += f[j][i];}*/}}}}long long ans = 0;for (int i = 0; i < n; i++) {ans += f[i][(1 << n) - 1];}cout << ans << "\n";return 0;
}

最短Hamilton路径

题意:

给你任意两点之间长度, 问你经过每个点恰好一次的最小长度是多少.

思路:

和TSP问题很相似, 不过这个是经过每个点恰好一次, 那个是最后还要回到原点, 因此那个得套Floyd.

乍一看属于常规思路: 我们用f[i][j]的一个维度表示当前节点, 另一个表示状态. 即f[i][j]表示当前租到了i节点, 状态j的方案数量.经典步骤如下:

1)memset f 无穷大, 特判下起点是f[0][1] = 0, 节点0, 状态1的情况.一定不存在, (相当于没出发怎么会存在)

2)和TSP问题一样的dp过程

3)输出以n-1为终点的状态为(1<<n)-1的结果即可. 因为最终一定会走到n-1这个节点!

code:

#include <bits/stdc++.h>using namespace std;int g[25][25];
long long f[25][1 << 20];signed main() {ios::sync_with_stdio(false);cin.tie(0);int n;cin >> n;memset(f, 127, sizeof f);    f[0][1] = 0;for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) cin >> g[i][j];//dpfor (int S = 0; S < (1 << n); S++)  //stausfor (int i = 0; i < n; i++) if (S & (1 << i)) //fromfor (int j = 0; j < n; j++) if (S & (1 << j)) //tof[j][S] = min(f[j][S], f[i][S ^ (1 << j)] + g[i][j]);cout << f[n - 1][(1 << n) - 1] << endl;return 0;
}

P3052 [USACO12MAR]Cows in a Skyscraper G

题意:

给你n个物品, 每个重量为s[i], 给你每个组的重量上限m, 问你最少分多少个组?

思路:

本题属于反套路型的, 一般人会用dfs来做.

如果用状压需要两个存状态的一维数组 f:i状态最小坐组数量 i状态最小重量

注意更新时加的那个限制条件, 很恶心人的. 加第二个限制条件是因为要更新f为更小的!

code:

#include <bits/stdc++.h>using namespace std;long long f[1 << 18]; //i状态最小坐数量
long long g[1 << 18]; //i状态最小重量signed main() {ios::sync_with_stdio(false);cin.tie(0);int n, m;cin >> n >> m;vector<long long> a(n);for (int i = 0; i < n; i++) cin >> a[i];memset(f, 127, sizeof f); memset(g, 127, sizeof g);f[0] = 1; f[0] = 0;for (int i = 0; i < (1 << n); i++)  //枚举状态for (int j = 0; j < n; j++) if (!((1 << j) & i)) {//枚举具体的人 //能放这个组 并且要开新的组if (g[i] + a[j] > m && f[i | (1 << j)] >= f[i] + 1) {f[i | (1 << j)] = f[i] + 1;g[i | (1 << j)] = min(a[j], g[i | (1 << j)]);}//不能放这个组 并且不用开新的组if (g[i] + a[j] <= m && f[i | (1 << j)] >= f[i]) {f[i | (1 << j)] = f[i];g[i | (1 << j)] = min(g[i] + a[j], g[i | (1 << j)]);}    }cout << f[(1 << n) - 1] << "\n";return 0;
}

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

题意:

思路:

这个题目很恶心的, 有时间补下, 当时没做出来...

code:

#include <bits/stdc++.h>using namespace std;
const double eps = 1e-8;int f[1<<19], st[405];
double x[25], y[25];
int n, m, top;void solve() {cin >> n >> m;memset(f, 127, sizeof f); memset(st, 0, sizeof st);f[0] = 0; top = 0;for (int i = 1; i <= n; i++)cin >> x[i] >> y[i];//找抛物线for (int i = 1; i <= n; i++) {for (int j = i + 1; j <= n; j++) {if (x[i] == x[j])continue;double ta = (y[i] - y[j] * x[i] / x[j]) / x[i] / (x[i] - x[j]);double tb = (y[i] * x[j] * x[j] / x[i] / x[i] - y[j]) / (x[j] * x[j] / x[i] - x[j]);if (ta < 0) {top ++;//找能被打掉的猪for (int k = 1; k <= n; k++)if (fabs(ta * x[k] * x[k] + tb * x[k] - y[k]) <= eps)st[top] |= (1 << (k - 1));}}}//枚举状态for (int k = 0; k < (1 << n); k++) {for (int i = 1; i <= top; i++)f[k | st[i]] = min(f[k | st[i]], f[k] + 1);for (int i = 1; i <= n; i++)f[k | (1 << (i - 1))] = min(f[k | (1 << (i - 1))], f[k] + 1);}cout << f[(1 << n) - 1] << "\n";}signed main() {ios::sync_with_stdio(false);cin.tie(nullptr);int t;cin >> t;while (t--) {solve();}return 0;}

P3959 [NOIP2017 提高组] 宝藏

题意:

思路:

首先我们要知道枚举子集的技巧

for (int son = S; son; son = (son - 1) & S) {// son 为 S 的子集
}

其次这个题目就是个**题, 恶心人有一手的.(我智商低...

基本参照yxc的写的, 过一段时间回来补吧.

code:

#include <bits/stdc++.h>using namespace std;const int INF = 0x3f3f3f3f;int f[1 << 12][12], g[1 << 12];
int dis[12][12];
int n, m;void solve() {cin >> n >> m;memset(dis, 0x3f, sizeof dis);memset(f, 0x3f, sizeof f);for (int i = 0; i < n; i++) {dis[i][i] = 0;}for (int i = 0; i < m; i++) {int u, v, w;cin >> u >> v >> w;u --, v--;dis[u][v] = dis[v][u] = min(w, dis[u][v]);}for (int i = 1; i < 1 << n; i++)for (int j = 0; j < n; j++) if (i >> j & 1)for (int k = 0; k < n; k++)if (dis[j][k] != INF)g[i] |= 1 << k;for (int i = 0; i < n; i++) {f[1 << i][0] = 0;}for (int i = 1; i < 1 << n; i++) {for (int j = (i - 1); j; j = (j - 1) & i) {if ((g[j] & i) == i) {int remain = i ^ j;int cost = 0;for (int k = 0; k < n; k++)if (remain >> k & 1) {int t = INF;for (int u = 0; u < n; u++)if (j >> u & 1)t = min(t, dis[k][u]);cost += t;}for (int k = 1; k < n; k ++ ) {f[i][k] = min(f[i][k], f[j][k - 1] + cost * k);}}}}int res = INF;for (int i = 0; i < n; i ++ ) {res = min(res, f[(1 << n) - 1][i]);}cout << res << "\n";}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);solve();return 0;
}

AC Challenge2018 南京网络赛 E

题意:

有很多道问题,在做某一道题前,要先完成某些题目,做出题目会得到指定的分数,分数可能是负数,并且分数跟做出这道题的时间有关.问你能得到的最大分数是多少?

思路:

枚举现在做出的题目为状态, 1的数量就是做出题目的数量, 根据题目要求进行位运算一同操作.

注意开long long

code:


#include <bits/stdc++.h>
#define int long longusing namespace std;const int INF = 0x3f3f3f3f;
int pre[1 << 22], f[1 << 22];
int a[25], b[25];
int n, m;//算二进制下的1个数
int cac(int S) {int cnt = 0;while (S) { if (S & 1) cnt ++; S >>= 1;} return cnt;}
typedef long long ll;ll count(ll x){ll num=0;for (int j=0;j<25;j++)if (x&(1<<j)) num++;return num;
}void solve() {cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i] >> b[i] >> m;while (m --) {int t;cin >> t;pre[i] |= (1 << (t - 1));}}for (int i = 0; i < (1 << n); i++) {f[i] = -INF;}f[0] = 0;int ans = 0;for (int i = 1; i < (1 << n); i++) {for (int j = 0; j < n; j++) {if (! ((1 << j) & i)) continue;//能够枚举子集int u = i ^ (1 << j);if ((u & pre[j + 1]) == pre[j + 1]) {int w = f[u] + cac(i) * a[j + 1] + b[j + 1];f[i] = max(f[i], w);ans = max(ans, f[i]);}}}cout << ans << "\n";
}signed main() {ios::sync_with_stdio(false);cin.tie(nullptr);solve();return 0;
}

二.高维前缀和 / 动态高维前缀和

Or Plus Max

题意:

求满足0 <= i, j <= 2^n-1, (i or j)<=k 条件的 ai + aj的最大值.

思路:

对于每个i枚举其超集, 更新超集中所包含子集的最大值, 同时更新k.

code:

#include<bits/stdc++.h>
using namespace std;int n;
int a[300010];
int MAX[300010];
int ans[300010];signed main() {cin >> n;for (int i = 0;i < (1 << n); i++) cin >> a[i];for (int i = 0;i < (1 << n); i++) for (int j = i; j < (1 << n); j = (j+1)|i) {ans[j] = max(ans[j], MAX[j] + a[i]);MAX[j] = max(MAX[j], a[i]); }for (int i = 1; i < (1 << n); i++) {ans[i] = max(ans[i], ans[i - 1]);cout << ans[i] << "\n";}return 0;
}

Bits And Pieces

题意:

求出 ai|(aj&ak)的最大值,i < j < k

思路:

有些迷糊

f[i][j] -> 有多少个数可以将前j j位的某些位置1变为0,从而变为i.

因为是a_i | (a_j & a_k), 采用从高位向低位来枚举的时候.

1)a_i当前是1, j,k任意取, res更新

2)a_i当前是0, 若想让值更大 则(a_j & a_k)都为1时才能更大, 即子集里至少有两个都满足当前位位1才行, 这时候更新sta, res. 否则不更新.

3)更新a_i, 这里不太懂...

4)用res更新ans.

code:

#include <bits/stdc++.h>using namespace std;int f[1 << 21][21];
int a[1000005];void update(int num, int k) {if (k > 20) return;if (f[num][k] > 1) return;f[num][k] ++;update(num, k + 1);if ((num >> k) & 1)update(num ^ (1 << k), k);
}signed main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n;cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i];}int ans = 0;for (int i = n; i >= 1; i--) {int res = 0, sta = 0;for (int j = 20; j >= 0; j--) {if ((a[i] >> j) & 1) {res |= 1 << j;} else if (f[sta|(1 << j)][20] > 1) {res |= 1 << j, sta |= 1 << j;}}update(a[i], 0);if (i <= n - 2) {ans = max(ans, res);}}cout << ans << "\n";return 0;
}

Jzzhu and Numbers

题意:

待补

思路:

待补

code:

#include<bits/stdc++.h>using namespace std;const int MOD = 1000000007;
const int N = 1000000;int n;
int ksm[N+1];
int sum[1<<20];void init() {ksm[0]=1;for(int i=1;i<=n;i++)ksm[i]=2*ksm[i-1]%MOD;
}signed main(){cin >> n;init();for (int i = 1; i <= n; i++) {int t;cin >> t;t = (1 << 20) - 1 ^ t;sum[t] ++;}for (int i = 0; i < 20; i++) for (int j = 0; j < 1<<20; j++)if (j & (1 << i)) sum[j] += sum[j ^ (1<<i)];int ans = ksm[n];for (int i = 0; i < (1 << 20) - 1; i++)(ans += (__builtin_popcount(i) & 1 ? -1 : 1) * ksm[sum[i]]) %= MOD;cout << (ans + MOD) % MOD << endl;return 0;
}

Vowels

题意:

思路:

code:

Compatible Numbers

题意:

给你1~n个a_i, 对于每个a_i都要找到对应a_j, 使得a_i & a_j == 0, 若不能实现输出-1

思路:

code:

#include <bits/stdc++.h>using namespace std;int a[1000005],f[1<<22];
int n;int main(void) {cin >> n;memset(f,-1,sizeof(f));for (int i = 0;i < n; i++) {cin >> a[i];f[(1<<22) - 1 ^ a[i]] = a[i];}for (int i= (1<<22) - 1;i >= 0;i --) {if (f[i] >= 0) continue;for (int j = 0; j < 22; j++)if (f[i | (1<<j)] >= 0) {f[i] = f[i | (1<<j)];break;}}for (int i = 0; i < n; i++) {cout << f[a[i]] << " \n"[i == n - 1];}return 0;
}

三.轮廓线型 / 插头dp

状压dp个人刷题记录相关推荐

  1. Corn fields(玉米田)状压dp入门第一题 洛谷P1879 poj3254

    题目描述 Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ...

  2. 【2022国赛模拟】无损加密——LGV引理、状压DP

    原创题无来源 题目描述 题解 首先把问题稍作简化,我们可以最后把行列式乘上所有 dkd_kdk​ 的积的 nnn 次方,这样前面的过程就不用考虑 dkd_kdk​ 了,暴力也只需要变动 [lk,rk] ...

  3. 【BZOJ2073】[POI2004]PRZ 状压DP

    [BZOJ2073][POI2004]PRZ Description 一只队伍在爬山时碰到了雪崩,他们在逃跑时遇到了一座桥,他们要尽快的过桥. 桥已经很旧了, 所以它不能承受太重的东西. 任何时候队伍 ...

  4. ACM-ICPC 2018 南京赛区网络预赛 E. AC Challenge 状压dp

    Dlsj is competing in a contest with n(0<n≤20)n (0 < n \le 20)n(0<n≤20) problems. And he kno ...

  5. 【状压DP+高精】【cofun1370】走道铺砖问题

    [cofun1370]走道铺砖问题 Description 有一个专门为装修设计方案的设计师.在某一天,他接到了一个项目,为一栋正在修建的大楼设计走道的地板铺设方案.此项目的委托人事先便进行了说明:地 ...

  6. 刷题周记(九)——#状压DP:最短Hamilton路径、小国王(互不侵犯)、玉米田(Corn Fields G)、愤怒的小鸟、吃奶酪、炮兵阵地、宝藏 #区间DP:清空字符串#DP:关灯问题II

    文章目录 --2020年12月20日(周日)------------------ 状压DP 一.最短Hamilton路径(模板题) 二.玉米田(P1879 [USACO06NOV]Corn Field ...

  7. 糖果(2019第十届蓝桥杯省赛C++A组I题) 解题报告(状压dp) Apare_xzc

    糖果(2019第十届蓝桥杯省赛C++A组I题) 解题报告(状压dp) xzc 2019/4/5 试题 I: 糖果 时间限制: 1.0s 内存限制: 256.0MB 本题总分:25分 [问题描述]    ...

  8. 【每日DP】day2、P1879 [USACO06NOV]Corn Fields G玉米地(状压DP模板题)难度⭐⭐⭐★

    昨天的每日DP我还在写01背包,今天就到状压DP了,真刺激. P1879 [USACO06NOV]Corn Fields G 题目链接 输入 2 3 1 1 1 0 1 0 输出 9 一道简单的状压D ...

  9. 【思维题 状压dp】APC001F - XOR Tree

    可能算是道中规中矩的套路题吧-- Time limit : 2sec / Memory limit : 256MB Problem Statement You are given a tree wit ...

最新文章

  1. 使用ARouter实现组件化
  2. python3下载教程-Python3完全零基础入门精讲 全套视频教程
  3. Linux查看内存使用情况
  4. JavaScript深入【表达式和运算符(上集)】你能过我8关js运算符的题目吗?
  5. linux django搭建网站,Linux下搭建Django站点一
  6. Redkale 让你重新认识Java
  7. ES6_对象简洁语法_note
  8. Linux多进程编程(1)
  9. 用FreeBSD与memcached建立分布式缓存服务器全程记录之memcached使用与安装
  10. redhat7挂载光盘
  11. 联想小新触摸板驱动_联想笔记本触摸板驱动下载
  12. Python smtp拟人个性化群发邮件,imap退信批量处理和SuiteCRM结合使用问题
  13. 电感5大特性参数,你知道几个?
  14. Oracle 动态游标 PL/SQL 动态SQL语句 open for [using] 语句
  15. 故事要从我白嫖了一个阿里云服务器说起
  16. AGV资料学习参考 AGV调度系统地图建模参考文件 AGV调度系统源码(OpenTCS) AGV调度系统地 多AGV调度系统实现图建模参考文件c++
  17. 如何系统得对目标检测模型的误差分析?
  18. 企业级存储发展趋势谈:开源存储的冷思考
  19. fastadmin html模板,使用fastadmin的页面异常模板
  20. linux 欢迎语,一日一技 | 如何让你的终端欢迎语好看又有趣

热门文章

  1. wamp安装与环境配置
  2. Spring Boot –如何更改Tomcat端口
  3. 学习计算机需要要了解与掌握的计算机基础
  4. java class 静态模块_Java API 最佳设计实践:在模块化和非模块化 Java 环境中使用...
  5. 图片瀑布流ios中部分显示空白
  6. 车牌识别--铆钉的去除
  7. java 中的排序_浅谈java中常见的排序
  8. 高等数学学习笔记——第七十讲——方向导数与梯度
  9. {博学谷学习记录} 超强总结,用心分享|狂野架构师-前置互联网架构演变过程
  10. PHP 生成二维码保存数据库