Acwing 167. 木棒

深度优先搜索,剪枝(贪心)

题目希望我们找出最小的长度\(len\),使得木棒能够粘成\(n\)\(len\)的木棍。

首先遍历长度(范围小),但是请不要用二分,因为\(len\)不存在单调性和两段性。

暴力\(dfs\)一定是过不了的,那么我们先从拼接入手:

从一堆长度不等的木棒中,拼出已知长度的木棍,很容易让人联想到砝码称重的问题。

所以我们先从长到短排列所有木棍,这样\(dfs\)每次扫描都会从平方变为线性。

该题有三个性质:
(这里保证\(len\)整除总长度)

  1. 如果dfs判断一根木棒不能使方案达成,那么同等长度的木棒也一定不可以,这一点显而易见。

  2. 如果一根木棒是第一个被摆放的,并且也不能使方案成立,那么剩下还待拼接的\(k\)个木棍一定无法完整拼成,也就是所有的木棍都是等效的(因为这根木棍必定被使用,且其无法被正好拼接)。

  3. 如果最后一根木棒被接入,并且无法使整个方案成立,那么立即停止搜索。这一点是依据贪心,如果“用一根木棒可以填满”,那么一定“不用两根木棒去填”。也就是目前已是最佳状态,但不满足目标,故该层搜索状态舍去。

剩下的代码中加入一点小小的优化(相信你们可以看得懂,就不解释了)。

AC code

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
int a[N], n, len;
bool st[N];

bool dfs(int nown, int cnt, int u)
{
	if(cnt == 0) return true;
	if(len == nown)
		return dfs(0, cnt - 1, 0);
	
	for(int i = u; i < n; i ++)
		if(!st[i] && nown + a[i] <= len)
		{
			st[i] = true;
			if(dfs(nown + a[i], cnt, i + 1)) return true;
			st[i] = false;
			while(a[i] == a[i + 1]) i ++;
			if(!nown || nown + a[i] == len) return false;
		}
	
	return false;
}

int main()
{
	//freopen("in.txt", "r", stdin);
	//freopen("out.txt", "w", stdout);
	
	while(cin >> n && n)
	{
		int sum = 0, imax = 0;
		for(int i = 0; i < n; i ++ )
			cin >> a[i], sum += a[i], imax = max(imax, a[i]);
		
		sort(a, a + n);
		reverse(a, a + n);
		
		for(len = imax; len <= sum; len ++ )
		{
			if(sum % len) continue;
			int k = sum / len;
			memset(st, false, sizeof st);
			if(dfs(0, k, 0)) break;
		}
		cout << len << endl;
	}
	
	return 0;
}
posted @ 2023-01-23 17:09  Sankano  阅读(31)  评论(0编辑  收藏  举报