class080 状压dp-上【算法】

news2024/9/29 3:35:36

class080 状压dp-上【算法】

算法讲解080【必备】状压dp-上

在这里插入图片描述

在这里插入图片描述

Code1 464. 我能赢吗

// 我能赢吗
// 给定两个整数n和m
// 两个玩家可以轮流从公共整数池中抽取从1到n的整数(不放回)
// 抽取的整数会累加起来(两个玩家都算)
// 谁在自己的回合让累加和 >= m,谁获胜
// 若先出手的玩家能稳赢则返回true,否则返回false
// 假设两位玩家游戏时都绝顶聪明,可以全盘为自己打算
// 测试链接 : https://leetcode.cn/problems/can-i-win/

package class080;

// 我能赢吗
// 给定两个整数n和m
// 两个玩家可以轮流从公共整数池中抽取从1到n的整数(不放回)
// 抽取的整数会累加起来(两个玩家都算)
// 谁在自己的回合让累加和 >= m,谁获胜
// 若先出手的玩家能稳赢则返回true,否则返回false
// 假设两位玩家游戏时都绝顶聪明,可以全盘为自己打算
// 测试链接 : https://leetcode.cn/problems/can-i-win/
public class Code01_CanIWin {

	public static boolean canIWin(int n, int m) {
		if (m == 0) {
			// 来自题目规定
			return true;
		}
		if (n * (n + 1) / 2 < m) {
			// 如果1~n数字全加起来
			// 累加和和是n * (n+1) / 2,都小于m
			// 那么不会有赢家,也就意味着先手不会获胜
			return false;
		}
		// dp[status] == 0 代表没算过
		// dp[status] == 1 算过,答案是true
		// dp[status] == -1 算过,答案是false
		int[] dp = new int[1 << (n + 1)];
		return f(n, (1 << (n + 1)) - 1, m, dp);
	}

	// 如果1~7范围的数字都能选,那么status的状态为:
	// 1 1 1 1 1 1 1 1
	// 7 6 5 4 3 2 1 0
	// 0位弃而不用
	// 如果1~7范围的数字,4、2已经选了不能再选,那么status的状态为:
	// 1 1 1 0 1 0 1 1
	// 7 6 5 4 3 2 1 0
	// 0位弃而不用
	// f的含义 :
	// 数字范围1~n,当前的先手,面对status给定的数字状态
	// 在累加和还剩rest的情况下
	// 返回当前的先手能不能赢,能赢返回true,不能赢返回false
	public static boolean f(int n, int status, int rest, int[] dp) {
		if (rest <= 0) {
			return false;
		}
		if (dp[status] != 0) {
			return dp[status] == 1;
		}
		// rest > 0
		boolean ans = false;
		for (int i = 1; i <= n; i++) {
			// 考察所有数字,但是不能选择之前选了的数字
			if ((status & (1 << i)) != 0 && !f(n, (status ^ (1 << i)), rest - i, dp)) {
				ans = true;
				break;
			}
		}
		dp[status] = ans ? 1 : -1;
		return ans;
	}

}

Code2 473. 火柴拼正方形

// 火柴拼正方形
// 你将得到一个整数数组 matchsticks
// 其中 matchsticks[i] 是第 i 个火柴棒的长度
// 你要用 所有的火柴棍 拼成一个正方形
// 你 不能折断 任何一根火柴棒,但你可以把它们连在一起,而且每根火柴棒必须 使用一次
// 如果你能拼出正方形,则返回 true ,否则返回 false
// 测试链接 : https://leetcode.cn/problems/matchsticks-to-square/

f(int status, int cur, int rest)
对于每一个没使用的火柴,(status&(1<<i))!=0
cur+nums[i]<=limit,并且不超过边长限制
if(cur+nums[i]==0)
	f(status^(1<<i),0,rest-1)
else
	f(status^(1<<i),cur+nums[i],rest)
package class080;

// 火柴拼正方形
// 你将得到一个整数数组 matchsticks
// 其中 matchsticks[i] 是第 i 个火柴棒的长度
// 你要用 所有的火柴棍 拼成一个正方形
// 你 不能折断 任何一根火柴棒,但你可以把它们连在一起,而且每根火柴棒必须 使用一次
// 如果你能拼出正方形,则返回 true ,否则返回 false
// 测试链接 : https://leetcode.cn/problems/matchsticks-to-square/
public class Code02_MatchsticksToSquare {

	public static boolean makesquare(int[] nums) {
		int sum = 0;
		for (int num : nums) {
			sum += num;
		}
		if (sum % 4 != 0) {
			return false;
		}
		int n = nums.length;
		int[] dp = new int[1 << n];
		return f(nums, sum / 4, (1 << n) - 1, 0, 4, dp);
	}

	// limit : 每条边规定的长度
	// status : 表示哪些数字还可以选
	// cur : 当前要解决的这条边已经形成的长度
	// rest : 一共还有几条边没有解决
	// 返回 : 能否用光所有火柴去解决剩下的所有边
	// 因为调用子过程之前,一定保证每条边累加起来都不超过limit
	// 所以status是决定cur和rest的,关键可变参数只有status
	public static boolean f(int[] nums, int limit, int status, int cur, int rest, int[] dp) {
		if (rest == 0) {
			return status == 0;
		}
		if (dp[status] != 0) {
			return dp[status] == 1;
		}
		boolean ans = false;
		for (int i = 0; i < nums.length; i++) {
			// 考察每一根火柴,只能使用状态为1的火柴
			if ((status & (1 << i)) != 0 && cur + nums[i] <= limit) {
				if (cur + nums[i] == limit) {
					ans = f(nums, limit, status ^ (1 << i), 0, rest - 1, dp);
				} else {
					ans = f(nums, limit, status ^ (1 << i), cur + nums[i], rest, dp);
				}
				if (ans) {
					break;
				}
			}
		}
		dp[status] = ans ? 1 : -1;
		return ans;
	}

}

Code3 698. 划分为k个相等的子集

// 划分为k个相等的子集
// 给定一个整数数组 nums 和一个正整数 k,
// 找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。
// 测试链接 : https://leetcode.cn/problems/partition-to-k-equal-sum-subsets/

package class080;

import java.util.Arrays;

// 划分为k个相等的子集
// 给定一个整数数组  nums 和一个正整数 k,
// 找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。
// 测试链接 : https://leetcode.cn/problems/partition-to-k-equal-sum-subsets/
public class Code03_PartitionToKEqualSumSubsets {

	// 状压dp的解法
	// 这是最正式的解
	public static boolean canPartitionKSubsets1(int[] nums, int k) {
		int sum = 0;
		for (int num : nums) {
			sum += num;
		}
		if (sum % k != 0) {
			return false;
		}
		int n = nums.length;
		int[] dp = new int[1 << n];
		return f1(nums, sum / k, (1 << n) - 1, 0, k, dp);
	}

	// 就是题目2的递归函数
	public static boolean f1(int[] nums, int limit, int status, int cur, int rest, int[] dp) {
		if (rest == 0) {
			return status == 0;
		}
		if (dp[status] != 0) {
			return dp[status] == 1;
		}
		boolean ans = false;
		for (int i = 0; i < nums.length; i++) {
			if ((status & (1 << i)) != 0 && cur + nums[i] <= limit) {
				if (cur + nums[i] == limit) {
					ans = f1(nums, limit, status ^ (1 << i), 0, rest - 1, dp);
				} else {
					ans = f1(nums, limit, status ^ (1 << i), cur + nums[i], rest, dp);
				}
				if (ans) {
					break;
				}
			}
		}
		dp[status] = ans ? 1 : -1;
		return ans;
	}

	// 纯暴力的递归(不做任何动态规划),利用良好的剪枝策略,可以做到非常好的效率
	// 但这并不是正式的解,如果数据设计的很苛刻,是通过不了的
	public static boolean canPartitionKSubsets2(int[] nums, int k) {
		int sum = 0;
		for (int num : nums) {
			sum += num;
		}
		if (sum % k != 0) {
			return false;
		}
		int n = nums.length;
		Arrays.sort(nums);
		return f2(new int[k], sum / k, nums, n - 1);
	}

	// group里面是各个集合已经有的累加和
	// 随着递归的展开,group里的累加和会变化
	// 所以这是一个带路径的递归,而且路径信息比较复杂(group数组)
	// 无法改成动态规划,但是利用剪枝策略可以通过
	// group[0....index]这些数字,填入每个集合,一定要都使用
	// 每个集合的累加和一定都要是target,返回能不能做到
	public static boolean f2(int[] group, int target, int[] nums, int index) {
		if (index < 0) {
			return true;
		}
		int num = nums[index];
		int len = group.length;
		for (int i = 0; i < len; i++) {
			if (group[i] + num <= target) {
				// 当前数字num放进i号集合
				group[i] += num;
				if (f2(group, target, nums, index - 1)) {
					return true;
				}
				// 递归完成后将路径还原
				group[i] -= num;
				while (i + 1 < group.length && group[i] == group[i + 1]) {
					i++;
				}
			}
		}
		return false;
	}

}

Code4 P1171 售货员的难题

// 售货员的难题 - TSP问题
// 某乡有n个村庄(1<=n<=20),有一个售货员,他要到各个村庄去售货
// 各村庄之间的路程s(1<=s<=1000)是已知的
// 且A村到B村的路程,与B到A的路大多不同(有向带权图)
// 为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,
// 假设商店所在的村庄为1
// 他不知道选择什么样的路线才能使所走的路程最短
// 请你帮他选择一条最短的路
// 测试链接 : https://www.luogu.com.cn/problem/P1171
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code,提交时请把类名改成"Main",可以直接通过

package class080;

// 售货员的难题 - TSP问题
// 某乡有n个村庄(1<=n<=20),有一个售货员,他要到各个村庄去售货
// 各村庄之间的路程s(1<=s<=1000)是已知的
// 且A村到B村的路程,与B到A的路大多不同(有向带权图)
// 为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,
// 假设商店所在的村庄为1
// 他不知道选择什么样的路线才能使所走的路程最短
// 请你帮他选择一条最短的路
// 测试链接 : https://www.luogu.com.cn/problem/P1171
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code,提交时请把类名改成"Main",可以直接通过

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

// 正常来说把MAXN改成20能通过,实现是正确的
// 问题是卡空间,而且c++的实现不卡空间,就卡java的实现
// 但如果把MAXN改成19,会有一个测试用例通过不了
// 那就差这么一点空间...看不起java是吗?
// 好,你歧视java实现,那就别怪我了
// 完全能通过的版本看Code04_TSP2的实现
public class Code04_TSP1 {

	public static int MAXN = 19;

	public static int[][] graph = new int[MAXN][MAXN];

	public static int[][] dp = new int[1 << MAXN][MAXN];

	public static int n;

	public static void build() {
		for (int s = 0; s < (1 << n); s++) {
			for (int i = 0; i < n; i++) {
				dp[s][i] = -1;
			}
		}
	}

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer in = new StreamTokenizer(br);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		while (in.nextToken() != StreamTokenizer.TT_EOF) {
			n = (int) in.nval;
			build();
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < n; j++) {
					in.nextToken();
					graph[i][j] = (int) in.nval;
				}
			}
			out.println(compute());
		}
		out.flush();
		out.close();
		br.close();
	}

	public static int compute() {
		return f(1, 0);
	}

	// s : 村里走没走过的状态,1走过了不要再走了,0可以走
	// i : 目前在哪个村
	public static int f(int s, int i) {
		if (s == (1 << n) - 1) {
			// n : 000011111
			return graph[i][0];
		}
		if (dp[s][i] != -1) {
			return dp[s][i];
		}
		int ans = Integer.MAX_VALUE;
		for (int j = 0; j < n; j++) {
			// 0...n-1这些村,都看看是不是下一个落脚点
			if ((s & (1 << j)) == 0) {
				ans = Math.min(ans, graph[i][j] + f(s | (1 << j), j));
			}
		}
		dp[s][i] = ans;
		return ans;
	}

}

package class080;

// 售货员的难题 - TSP问题
// 某乡有n个村庄(1<=n<=20),有一个售货员,他要到各个村庄去售货
// 各村庄之间的路程s(1<=s<=1000)是已知的
// 且A村到B村的路程,与B到A的路大多不同(有向带权图)
// 为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,
// 假设商店所在的村庄为1
// 他不知道选择什么样的路线才能使所走的路程最短
// 请你帮他选择一条最短的路
// 测试链接 : https://www.luogu.com.cn/problem/P1171
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code,提交时请把类名改成"Main",可以直接通过

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

// 卡空间是吧?绕一下!
public class Code04_TSP2 {

	public static int MAXN = 19;

	public static int[] start = new int[MAXN];

	public static int[] back = new int[MAXN];

	// 这个图中,其实是不算起始村的,其他村庄彼此到达的路径长度
	public static int[][] graph = new int[MAXN][MAXN];

	// 不算起始村庄的
	public static int[][] dp = new int[1 << MAXN][MAXN];

	public static int n;

	public static void build() {
		for (int s = 0; s < (1 << n); s++) {
			for (int i = 0; i < n; i++) {
				dp[s][i] = -1;
			}
		}
	}

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer in = new StreamTokenizer(br);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		while (in.nextToken() != StreamTokenizer.TT_EOF) {
			n = (int) in.nval - 1;
			build();
			in.nextToken();
			for (int i = 0; i < n; i++) {
				in.nextToken();
				start[i] = (int) in.nval;
			}
			for (int i = 0; i < n; i++) {
				in.nextToken();
				back[i] = (int) in.nval;
				for (int j = 0; j < n; j++) {
					in.nextToken();
					graph[i][j] = (int) in.nval;
				}
			}
			out.println(compute());
		}
		out.flush();
		out.close();
		br.close();
	}

	public static int compute() {
		int ans = Integer.MAX_VALUE;
		// 起始村无编号
		for (int i = 0; i < n; i++) {
			// 起始村 -> i号村  +  i号村出发所有村子都走最终回到起始村
			ans = Math.min(ans, start[i] + f(1 << i, i));
		}
		return ans;
	}

	// s : 不包含起始村的
	public static int f(int s, int i) {
		if (s == (1 << n) - 1) {
			return back[i];
		}
		if (dp[s][i] != -1) {
			return dp[s][i];
		}
		int ans = Integer.MAX_VALUE;
		for (int j = 0; j < n; j++) {
			if ((s & (1 << j)) == 0) {
				ans = Math.min(ans, graph[i][j] + f(s | (1 << j), j));
			}
		}
		dp[s][i] = ans;
		return ans;
	}

}

2023-12-01 20:09:40

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1309600.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python】人工智能-机器学习——不调库手撕深度网络分类问题

1. 作业内容描述 1.1 背景 数据集大小150该数据有4个属性&#xff0c;分别如下 Sepal.Length&#xff1a;花萼长度(cm)Sepal.Width&#xff1a;花萼宽度单位(cm)Petal.Length&#xff1a;花瓣长度(cm)Petal.Width&#xff1a;花瓣宽度(cm)category&#xff1a;类别&#xff0…

flutter调试器查看不了副页面(非主页面/子页面)

刚接触flutter&#xff0c;写了两个页面&#xff0c;通过按钮&#xff0c;可以从主页面跳转到副页面&#xff0c;副页面我自己写的一个独立的dart文件&#xff0c;在主页面的代码中导入使用。但是当我运行代码后&#xff0c;点击跳转的时候&#xff0c;却发现查看不到对应的副页…

用CHAT了解生活中的化学

问CHAT&#xff1a;生活中有哪些常见的氢氧化合物&#xff1f; CHAT回复&#xff1a;生活中常见的氢氧化合物有以下几种&#xff1a; 1. 氢氧化钠(NaOH)&#xff1a;也叫苛性钠&#xff0c;是一种强碱性物质&#xff0c;主要用于造纸、肥皂制作、人造纤维和棉纺等工业中。 2.…

php实现截取姓名中的第一个字作为头像的实战记录

php 截取中文字符串第一个字 substr 函数 在 PHP 中&#xff0c;使用 substr 函数来截取中文字符串的第一个字。由于 PHP 默认的字符编码是 UTF-8&#xff0c;它可以正确处理中文字符。 $chineseString "你好世界"; $firstChar substr($chineseString, 0, 1); e…

TCP对数据的拆分

应用程序的数据一般都比较大&#xff0c;因此TCP会按照网络包的大小对数据进行拆分。 当发送缓冲区中的数据超过MSS的长度&#xff0c;数据会被以MSS长度为单位进行拆分&#xff0c;拆分出来的数据块被放进单独的网路包中。 根据发送缓冲区中的数据拆分情况&#xff0c;当判断…

ERP主要是干什么的

在信息技术快速发展的背景下&#xff0c;很多企业为了应对这些技术带来的机遇和挑战&#xff0c;也为了提高企业的竞争力和效率&#xff0c;提高企业管理水平&#xff0c;选择使用ERP系统来帮助企业进一步的发展。但是也有部分企业没明白ERP到底是干什么的&#xff0c;所以有些…

数据结构:图的存储和遍历

文章目录 图的存储结构邻接矩阵邻接矩阵的存储模拟实现 邻接表邻接表的模拟实现 图的遍历DFS和BFS遍历 图的存储结构和遍历的实现 图也是一种数据结构&#xff0c;在实际生活中有广泛运用&#xff0c;因此本篇总结的就是图的存储等 图的存储结构 在图中既有节点&#xff0c;也…

【docker 】Dockerfile指令学习

学习文档地址 上篇文章&#xff1a;【docker 】基于Dockerfile创建镜像 Dockerfile指令文档地址 .dockerignore 文件 Dockerfile指令 常见的指令 Dockerfile 指令说明FROM指定基础镜像&#xff0c;用于后续的指令构建。MAINTAINER指定Dockerfile的作者/维护者。&#xff…

代码审计零基础入门之思路篇

0x01 前言 ThinkPHP 是一款开源的 PHP 框架&#xff0c;用于快速、简单地开发 PHP 应用程序。它提供了一套丰富的功能和工具&#xff0c;使开发者能够更容易地构建各种规模的 Web 应用。ThinkPHP 的目标是提高开发效率&#xff0c;同时保持代码的可读性和可维护性。thinkphp的…

mysql:在字符串类型的列上创建索引,建议指定索引前缀长度

https://dev.mysql.com/doc/refman/8.2/en/create-index.html#create-index-column-prefixes 在字符串类型的列上创建索引&#xff0c;建议指定索引前缀长度&#xff0c;而没有必要用整个列来创建索引。因为用前面的字符创建索引&#xff0c;查询时并不会比在整列上创建索引慢很…

绿盟 SAS堡垒机 local_user.php 权限绕过漏洞复现

0x01 产品简介 SAS 安全审计系统是绿盟科技开发的一款堡垒机。 0x02 漏洞概述 绿盟 SAS堡垒机 local_user.php接口处存在权限绕过漏洞,未经身份认证的攻击者可以访问他们通常无权访问的敏感资源,最终导致系统处于极度不安全状态。 0x03 复现环境 FOFA: body="/ne…

土壤科学灌溉CG-36 土壤水势传感器

土壤科学灌溉CG-36 土壤水势传感器产品概述 土壤水势传感器可以很方便地插入到土壤剖面坑中&#xff0c;在其周围包裹上湿土即可。测定和记录非常简单。免维护、无需校准即可测量较大范围的土壤水势&#xff1b;无需灌水&#xff0c;大量程使得它成为测量自然系统水势的理想传…

javacv的视频截图功能

之前做了一个资源库的小项目&#xff0c;因为上传资源文件包含视频等附件&#xff0c;所以就需要时用到这个功能。通过对视频截图&#xff0c;然后作为封面缩略图&#xff0c;达到美观效果。 首先呢&#xff0c;需要准备相关的jar包&#xff0c;之前我用的是低版本的1.4.2&…

spring boot 实现直播聊天室(二)

spring boot 实现直播聊天室(二) 技术方案: spring bootnettyrabbitmq 目录结构 引入依赖 <dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.96.Final</version> </dependency>Si…

位1的个数

题目链接 位1的个数 题目描述 注意点 输入必须是长度为 32 的 二进制串 解答思路 位运算判断每一位是否为1 代码 public class Solution {// you need to treat n as an unsigned valuepublic int hammingWeight(int n) {int res 0;for (int i 0; i < 32; i) {res …

Mac中nvm切换node版本失败

Mac中使用 nvm 管理 node 版本&#xff0c;在使用指令&#xff1a;nvm use XXX 切换版本之后。 关闭终端&#xff0c;再次打开&#xff0c;输入 node -v 还是得到之前的 node 版本。 原因&#xff1a; 在这里这个 default 中有个 node 的版本号&#xff0c;使用 nvm use 时&a…

【玩转TableAgent数据智能分析】会话式数据分析,所需即所得!

目录 1 TableAgent介绍 2 TableAgent五大优点 3 体验TableAgent 3.1 登录TableAgent平台 3.2 会话式数据分析 4 总结 【优化改善】 【对比TableAgent与文心一言- E言易图】 1 TableAgent介绍 TableAgent是一款数据集成和分析平台&#xff0c;它可以帮助用户从多个数据源中…

【wimdows电脑上管理员账户与管理员身份的区别】

管理员账户 在控制面板的用户账户中&#xff0c;点击更改账户类型&#xff0c;可以看到目前的账户是“管理员账户”还是“标准账户”。 管理员身份 在快捷方式上右击&#xff0c;可以看到&#xff0c;可以选择以管理员身份运行该软件。 如何查看某个应用是否以管理员身份…

大数据云计算之OpenStack

大数据云计算之OpenStack 1.什么是OpenStack&#xff0c;其作用是什么&#xff1f;OpenStack主要的组成模块有哪些&#xff1f;各自的主要作用是什么&#xff1f; OpenStack是一个开源的云计算平台&#xff0c;旨在为企业和服务提供商提供私有云和公有云的建设和管理解决方案…

MySQL第三方备份工具Percona XtraBackup

实验环境&#xff1a; CentOS7.9 准备软件&#xff1a;yum -y install https://repo.percona.com/yum/percona-release-latest.noarch.rpm 一、什么是Percona XtraBackup&#xff1a;Percona XtraBackup&#xff08;简称PXB&#xff09;是 Percona 公司开发的一个用于 MySQL …