寒假集训·子集枚举2

寒假集训·状压DP子集枚举2

P3052的加强版

与原版不同,这里的数据范围从最大为 181818 变为了 222222 ,这就意味着我们只能使用 2n∗n2^n*n2n∗n 的时间复杂度,那我们就必须把dp数组变成一维。

dp[s]dp[s]dp[s] 就表示在 sss 状态下,所需的最小电梯数;

因为我们还要看能不能再装一头牛,所以我们还需要存最后一个电梯的重量。

cnt[s]cnt[s]cnt[s] 就表示在 sss 状态下,最后一个电梯的已用最小空间。

还有一个需要注意的点

dp[0]=1;dp[0]=1;dp[0]=1;

因为这里 cnt[0]=0cnt[0]=0cnt[0]=0,意思就是最后一个(第一个)电梯装了 000 磅,那也应该写用了1个电梯。

代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int n, m;
int c[25];
int dp[(1 << 22) + 5];
int cnt[(1 << 22) + 5];
int main() {
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		cin >> c[i];
	}
	memset(dp, 0x3f, sizeof dp);
	memset(cnt, 0x3f, sizeof cnt);
	dp[0] = 1;
	cnt[0] = 0;
	for (int s = 0; s < (1 << n); s++) {
		for (int i = 0; i < n; i++) {
			if (!(s & (1 << i))) {
				if (cnt[s] + c[i] <= m) {
					dp[s + (1 << i)] = min(dp[s + (1 << i)], dp[s]);
					cnt[s + (1 << i)] = min(cnt[s + (1 << i)], cnt[s] + c[i]);
				} else {
					dp[s + (1 << i)] = min(dp[s + (1 << i)], dp[s] + 1);
					cnt[s + (1 << i)] = min(cnt[s + (1 << i)], c[i]);
				}
			}
		}
	}
	for (int s = 0; s < (1 << n); s++) {
//		cout << "dp[" << s << "]: " << dp[s] << "  cnt[" << s << "]: " << cnt[s] << '\n';
	}
//	cout << '\n';
	cout << dp[(1 << n) - 1];
	return 0;
}

P5911 [POI 2004] PRZ

变式题,在上一道题的基础上计算答案的方法变成了取最慢的人,其他大概逻辑没有很大变化。

AT_dp_u Grouping

可以经过计算得出:此时时间复杂度为 3n3^n3n ,卡着过,所以枚举子集里不能有其他东西,但是我们又要获取状态 sss 的相性和,怎么办?预处理。

cpp 复制代码
for (int s = 0; s < (1 << n); s++) {  //状态
		for (int i = 0; i < n; i++) {  //枚举兔子i
			for (int j = i + 1; j < n; j++) {  //枚举兔子j
				if ((s & (1 << i)) && (s & (1 << j))) {  //都选了
					pre[s] += dis[i][j];
				}
			}
		}
	}

转移方程:
dp[s] = max(dp[s], dp[s ^ t] + pre[t]);

代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int dis[25][25];
ll pre[(1 << 16) + 5];
ll dp[(1 << 16) + 5];
int main() {
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cin >> dis[i][j];
		}
	}
	for (int s = 0; s < (1 << n); s++) {
		for (int i = 0; i < n; i++) {
			for (int j = i + 1; j < n; j++) {
				if ((s & (1 << i)) && (s & (1 << j))) {
					pre[s] += dis[i][j];
				}
			}
		}
	}
//	for (int s = 0; s < (1 << n); s++) {
//		cout << pre[s] << ' ';
//	}
//	cout << '\n';
	dp[0] = 0;
	for (int s = 0; s < (1 << n); s++) {
		for (int t = s; t; t = (t - 1)&s) {
			dp[s] = max(dp[s], dp[s ^ t] + pre[t]);
		}
	}
//	for (int s = 0; s < (1 << n); s++) {
//		cout << dp[s] << ' ';
//	}
//	cout << '\n';
	cout << dp[(1 << n) - 1];
	return 0;
}

P2167 [SDOI2009] Bill的挑战

定义 dp[i][mask]dp[i][mask]dp[i][mask] 表示构造到第 iii 位时,匹配的字符串集合为 maskmaskmask 的方案数;

预处理数组:f[i][ch]f[i][ch]f[i][ch] 表示第 iii 位填入字符 chchch 时,能匹配的字符串集合(二进制 maskmaskmask ),计算规则为:遍历所有字符串,若该字符串第 iii 位是 ??? 或等于 chchch,则对应位为 111;

状态转移:
dp[i+1][mask & f[i][ch]] += dp[i][mask]

当前位选字符 chchch 时,新的匹配状态为原状态与 f[i][ch]f[i][ch]f[i][ch] 的交集,方案数累加

初始化:dp[0][(1<<n)-1] = 1(第 000位未构造时,所有字符串都处于"匹配"状态,方案数为 111)。

P3694 邦邦的大合唱站队

前缀和数组 pre[j][i]pre[j][i]pre[j][i](0-based):表示原队列前 i+1i+1i+1 个位置(0~i)中,第 jjj 支乐队(0-based)的偶像数量,用于 O(1)O(1)O(1) 查询任意区间内某乐队的人数;

计数数组 f[j]f[j]f[j]:统计第 jjj 支乐队的总人数;

状态和数组 sum[mask]sum[mask]sum[mask]:表示 maskmaskmask 对应的乐队集合的总人数( maskmaskmask 为二进制状态,第 jjj 位为 111 表示选了第j支乐队)。

状态定义:dp[mask]dp[mask]dp[mask] 表示选 maskmaskmask 对应的乐队集合时,使这些乐队连续排列的最少出列人数;

初始化:dp[0] = 0(选 000 支乐队时出列人数为 000),其余初始化为极大值;

状态转移:遍历每个状态 maskmaskmask,尝试加入未选的乐队j,计算该乐队占据区间 [sum[mask][sum[mask][sum[mask], sum[mask]+f[j]-1]的出列成本(成本=区间长度−区间内原有的j乐队人数成本=区间长度-区间内原有的j乐队人数成本=区间长度−区间内原有的j乐队人数),更新dp[mask | (1<<j)] = min(原值, dp[mask]+成本)

相关推荐
Y.O.U..1 小时前
力扣刷题-61.旋转链表
算法·leetcode·链表
这波不该贪内存的2 小时前
【无标题】
算法·排序算法
kyle~2 小时前
ROS2----组件(Components)
开发语言·c++·机器人·ros2
靠沿2 小时前
【优选算法】专题二——滑动窗口
java·数据结构·算法
SoulruiA2 小时前
LeetCode-394. 字符串解码 递归思路
算法·leetcode·职场和发展
阿猿收手吧!2 小时前
【C++】Ranges 工厂视图与投影机制
开发语言·c++
.小墨迹2 小时前
局部规划中的TEB,DWA,EGOplanner等算法在自动驾驶中应用?
开发语言·c++·人工智能·学习·算法·机器学习·自动驾驶
求真求知的糖葫芦2 小时前
巴伦学习(三.一)一种可以实现阻抗变换的平面Marchand巴伦的公式推导学习笔记(下)(自用)
笔记·学习·平面
AI科技星2 小时前
张祥前统一场论 22 个核心公式及常数
服务器·人工智能·线性代数·算法·矩阵·概率论