Luogu P1441 砝码称重

Luogu P1441 砝码称重

题目链接

题意: 现有 n 个砝码,重量分别为 ai ​ ,在去掉 m 个砝码后,问最多能称量出多少不同的重量(不包括 0)。 请注意,砝码只能放在其中一边。

n20,m4,m<n,ai100

题目分析: 看到 n,m 的范围果断爆搜/状压,而对于当前的状态,计算称量出有多少种重量实际为01背包问题,也就是枚举到一个状态后01背包。

这里01背包我用了bitset优化,时间复杂度默认为 O(nw) (具体含义见 wiki )

算法一: 迭代加深搜索+记忆化 O(Cnmnaiw))

考虑枚举去掉哪个砝码,也就是 mn 为上界的 for 循环,但是这里 m 是给定的,可以进行迭代加深搜索。

记录 now 为当前状压的状态, h 为删掉了几个数,如果 h0 就01背包,否则找到一个没有删掉的砝码,删掉并且往下搜索,假设删掉了i,则有 now 中把 i 标记,h 变成 h1 后继续搜索。

核心代码:

int a[N];
bool f[1050000];
std::bitset<20010>vis;
void dfs(int now, int h) {
	if(f[now]) return ;
	f[now] = 1;
	if(h == 0) {
		vis.reset();
		vis |= 1;//空位一种可以转移走的状态 
		for(int i = 1; i <= n; ++i) {
			if((1 << i - 1) & now)
				vis |= vis << a[i];
		}
		ans = Max(ans, vis.count() - 1);//去掉空的状态,所以要-1 
	}
	else {
		for(int i = 1; i <= n; ++i)
			if((1 << i - 1) & now)
				dfs(now - (1 << i - 1), h - 1);
	}
}

算法二: 状压 O(Cnmnaiw)

枚举每一种选的状态(不管合不合法),通过计算该状态中 1 的数量(也就是砝码的数量)来判断合不合法,如果合法就01背包计算。

核心代码

inline int lowbit(int x) { return x & (-x); }//最后一位二进制数 
bool check(int x) {//判断是否有n-m个1 
	int cnt = 0;
	while(x) {
		++cnt;
		x -= lowbit(x);
		if(cnt > n - m) return 0;
	}
	return cnt == n - m;
}
int main() {
	n = read(); m = read();
	for(int i = 1; i <= n; ++i) a[i] = read();
	for(int i = 0; i <= (1 << n) - 1; ++i) {
		if(!check(i)) continue;
		vis &= 0; vis |= 1;
		for(int j = 1; j <= n; ++j)
			if(i & (1 << j - 1))
				vis |= vis << a[j];
		ans = Max(ans, vis.count() - 1);
	}
	printf("%d\n", ans);
	return 0;
}
posted @   do_while_true  阅读(160)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?

This blog has running: 1845 days 1 hours 34 minutes 8 seconds

点击右上角即可分享
微信分享提示