算法竞赛进阶指南第一天

这篇博客探讨了如何解决图论问题中最短Hamilton路径的二进制动态规划算法,以及利用位操作进行高效计算的技巧。同时介绍了递归实现指数型枚举的方法,用于在一定范围内生成所有可能的子集。文章涵盖了图的最短路径计算、位操作应用和递归枚举策略,展示了计算机科学中算法和数据结构的应用。

0x01

1.最短Hamilton路径(二进制状压dp)

题目链接

描述

给定一张 n(n≤20) 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。

输入格式

第一行一个整数n。

接下来n行每行n个整数,其中第i行第j个整数表示点i到j的距离(一个不超过10^7的正整数,记为a[i,j])。

对于任意的x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]>=a[x,z]。

输出格式

一个整数,表示最短Hamilton路径的长度。

我们定义f[i][j]表示“点被经过的状态“对应的二进制数为i(最低位为1表示编号为0的点被经过),当前处于点j时的最短路径,由此我们可知最终的答案为f[(1<<n)-1][n-1];

可知状态转移方程为f[i][j] = min(f[i][j],f[i^1<<j][k]+weight[k][j]);

其中i必须满足i>>j&1以及k必须满足i^1<<j>>k&1

代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
int weight[20][20];
int f[1 << 20][20];
int main() {
	int n;
	scanf("%d", &n);
	int x;
	for(int i=0;i<n;i++)
		for (int j = 0; j < n; j++) {
			scanf("%d", &x);
			weight[i][j] = x;
		}
	memset(f, 0x3f, sizeof(f));
	f[1][0] = 0;
	for (int i = 0; i <= 1 << n; i++)
		for (int j = 0; j < n; j++)if (i >> j & 1)
			for (int k = 0; k < n; k++)if (i ^ 1 << j >> k & 1)
				f[i][j] = min(f[i][j], f[i ^ 1 << j][k] + weight[k][j]);
	printf("%d\n", f[(1 << n) - 1][n - 1]);
	return 0;
}

2.成对变换

对于任意非负整数:

当n为偶数时,n xor 1 = n + 1;

当n为奇数时,n xor 1 =n - 1;

3.lowbit运算:

lowbit(n)定义为非负整数n在二进制表示下,“最低位的1以及其后面所有的0”构成的数值;

lowbit(n) = n & (-n)

证明过程如下:

对于任意非负整数n,将其取反,则n最低位1及其后面的0全部与~n相反,将其加1之后,前文所提部分变成了...1000000的样子,将(~n+1)&n即lowbit(n),又因为在补码状态下,~n=-n-1;

证毕

 对于1<<35以内的数字,可以用以下方法来以O(1)的时间复杂度来输出其所有1的位置

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
int H[37];
int main() {
	for (int i = 0; i <= 35; i++)H[(1LL << i) % 37] = i;
	int n;
	while (cin >> n) {
		while (n > 0) {
			printf("%d\n", H[(n & -n) % 37]);
			n -= (n & -n);
		}
	}
	return 0;
}

0x02

1.递归实现指数型枚举

从1~n随机选取任意多个,输出所有可能 

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
using namespace std;
int n; vector<int>v;
void solve(int x) {
	if (v.size() == n || x > n) {
		for (auto i : v)
			printf("%d ", i);
		printf("\n");
		return;
	}
	v.push_back(x);
	solve(x + 1);
	v.pop_back();
	solve(x + 1);
}
int main() {
	scanf("%d", &n);
	solve(1);
	return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值