Acwing 167. 木棒
深度优先搜索,剪枝(贪心)
题目希望我们找出最小的长度\(len\),使得木棒能够粘成\(n\)个\(len\)的木棍。
首先遍历长度(范围小),但是请不要用二分,因为\(len\)不存在单调性和两段性。
暴力\(dfs\)一定是过不了的,那么我们先从拼接入手:
从一堆长度不等的木棒中,拼出已知长度的木棍,很容易让人联想到砝码称重的问题。
所以我们先从长到短排列所有木棍,这样\(dfs\)每次扫描都会从平方变为线性。
该题有三个性质:
(这里保证\(len\)整除总长度)
-
如果dfs判断一根木棒不能使方案达成,那么同等长度的木棒也一定不可以,这一点显而易见。
-
如果一根木棒是第一个被摆放的,并且也不能使方案成立,那么剩下还待拼接的\(k\)个木棍一定无法完整拼成,也就是所有的木棍都是等效的(因为这根木棍必定被使用,且其无法被正好拼接)。
-
如果最后一根木棒被接入,并且无法使整个方案成立,那么立即停止搜索。这一点是依据贪心,如果“用一根木棒可以填满”,那么一定“不用两根木棒去填”。也就是目前已是最佳状态,但不满足目标,故该层搜索状态舍去。
剩下的代码中加入一点小小的优化(相信你们可以看得懂,就不解释了)。
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;
}
本文来自博客园,作者:{三季野花},转载请注明原文链接:https://www.cnblogs.com/SanGarden/articles/17065306.html