状压dp·

定义:

状压 dp 又叫集合动态规划。是以结合信息为状态的特殊的动态规划的问题。主要有传统集合动态规划 和基于连通性状态压缩的动态规划

状压dp

设计一个整型可变参数status,利用status的位信息,来表示:

某个样本是否还能使用,然后利用这个信息进行尝试

写出尝试的递归的函数 -> 记忆化搜索 -> 严格位置的动态规划 -> 空间压缩等优化

如果有k个样本,那么表示这些样本的状态,数量为2^k

样本每增加一个,状态的数量是指数级增长的,所以状压dp能解决的问题往往数据量不大

一般样本数量在20个以内

题目一

复制代码
# include <stdio.h>
# include <string.h>

//如果1~7范围的数字都能选,那么statue的状态为:
//1 1 1 1 1 1 1 1
//7 6 5 4 3 2 1 0
//0位弃而不用
//如果1~7范围的数字,4、2已经选了不能再选,那么statue的状态:
//1 1 1 0 1 0 1 1 
//7 6 5 4 3 2 1 0
//0位弃而不用
//f的含义:
//数字范围 1 ~ n,当前的先手,面对statue给定的数字状态
//在累加和还剩rest的情况下
//返回先手能不能赢,能赢返回true,不能赢返回false 

bool cmp(int n, int m, int* dp, int * statue)
{
	if (m <= 0)
		return false;
	if (dp[statue] != 0)
	{
		if (dp[statue] == 1)
			return true;
		else
			return false;
	}
	bool ans = false;
	for (int i=1; i<n; ++i)
	{
		//考察所有数字,但是不能选择之前选了的数字 
		if ((statue & (1<<i)) != 0 && cmp(n, m-i, dp, statue-(1<<i)) == true)
		{
			ans = true;
			break;
		}
	}
	if (ans == true)
		dp[statue] = 1;
	else
		dp[statue] = -1;
	return ans;
}

int main()
{
	int n;
	int m;
	scanf("%d %d", &n, &m);
	int dp[1<<(n+1)];
	int statue[1<<(n+1)-1]; //用二进制数表示该该数字是否还能使用,然后利用这个信息进行尝试 
	//dp[statue] == 0 代表没算过
	//dp[statue] == 1 算过,答案是true
	//dp[statue] == -1 算过,答案是false 
	memset(statue, 0, sizeof(statue));
} 

题目二

我们用下标处理matchsticks\[\]数组中的火柴棒长度

设计一个整型可变参数status,利用status的位信息,来表示:

某个下标对应的火柴是否还能使用,然后利用这个信息进行尝试

代码:

复制代码
# include <stdio.h>
// limit:每条边规定的长度
// statue: 表示哪些数字还可以选
// cur:当前要解决的这条边已经形成的长度
// rest:一共还有几条边没有解决
//返回:能否用光所有的火柴去解决剩下的所有边
//statue是决定cur和rest的,关键可变参数只有statue 
bool cmp(int n, int* s, int limit, int cur, int rest, int* dp, int statue)
{
	if (rest == 0)
	{
		if (statue == 0)
			return true;
		else
			return false;
	} 
	if (dp[statue] != 0)
	{
		if (dp[statue] == 1)
			return true;
		else
			return false;
	}
	bool ans = false;
	for (int i=0; i<n; ++i)
	{
		if ((statue & (1<<i)) == 1 && cur+s[i] <= limit)
		{
			if (cur + s[i] == limit)
				ans = cmp(n, s, limit, 0, rest-1, dp, statue-(1<<i));
			else
				ans = cmp(n, s, limit, cur+s[i], rest, dp, statue-(1<<i));
			if (ans == true)
				break;	
		}
	}
	if (ans == true)
		dp[statue] = 1;
	else
		dp[statue] = -1;
	return ans;
}

int main()
{
	int n;
	scanf("%d", &n);
	int s[n];
	int sum = 0;
	for (int i=0; i<n; ++i)
	{
		scanf("%d", &s[i]);
		sum = sum + s[i];
	}
	int dp[1<<(n+1)];
	int statue = 1<<(n+1)-1;
	sum = sum/4;
	int ans = cmp(n, s, sum, 0, dp, statue);
}

TSP问题

设计一个整型可变参数status,利用status的位信息,来表示:

某个下标对应的村庄是否到达过,然后利用这个信息进行尝试,取min

复制代码
# include <stdio.h>
int n;
 //s表示 statue(用于表示村子是否走过,如果走过则为1) 
 //x表示 目前在哪个村 
int cmp(int s, int x)
{
	if (s == (1<<n)-1)
		//如果村子都走完了 
		return gragh[x][0];
		
	if (dp[s][x]!=-1)
		return dp[s][x];
	int ans = 99999;
	for (int j=1; j<n; ++j)
	{
		//1...n-1这些村子中,查看是否有下一个落脚点 
		if ((s & (1<<j)) == 0) //当第 j 位(位信息)的 s 为0时,表示没有到达过 
			ans = min(ans, graph[x][j] + cmp(s|(1<<j), j));
	}
	dp[s][x] = ans;
	return ans;
}

int main()
{
	scanf("%d", &n);
	int dp[n][n];
	memset(dp, -1, sizeof(dp));
	int gragh[n][n];
	for (int i=0; i<n; ++i)
		for (int j=0; j<n; ++j)
			scanf("%d", &gragh[i][j]);
	int ans = cmp(1, 0)//1的二进制为000001,其中的1位于第0位,表示第0位(也就是起点)已经到达 
	
}
相关推荐
旋生万物6 分钟前
Agent System Prompt 注入螺旋公理:让 AI 推理不跑偏的可复制模板(附完整 Prompt)
人工智能·算法
深圳雨林凯AI22 分钟前
图案裂变的空间关系原理:元素放大为何必然挤压负空间,以及三个可调变量|雨林凯AI技术拆解
人工智能·算法
tellmewhoisi37 分钟前
机器学习:聚类算法
算法·机器学习·聚类
yi0111 小时前
LeetCode 134 加油站:从 双循环暴力 演变到 贪心
linux·算法·leetcode
sogw-三叶草️1 小时前
LeetCode 989. 数组形式的整数加法
数据结构·算法
维克兜率天2 小时前
【维克】均值回归:跌多了会涨,涨多了会跌
开发语言·笔记·python·算法·均值算法·回归·量化
yi0112 小时前
DAY 14: LeetCode 394. 字符串解码|递归和栈到底怎么处理嵌套?
数据结构·笔记·python·算法·leetcode
P.D.Wei2 小时前
[Leetcode 1614] 括号的最大嵌套深度
算法·leetcode·职场和发展
红姐跨境书3 小时前
AI 的底层逻辑:从数据、算法到智能的本质
人工智能·算法
繁华的地方不一定留下你的脚印3 小时前
C++ 算法与 ranges:用 find_if、transform 写清数据处理意图
开发语言·c++·算法