P1120 小木棍 [数据加强版]

题目描述

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。

现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。

给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

输入输出格式

输入格式:

 

输入文件共有二行。

第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤65

(管理员注:要把超过50的长度自觉过滤掉,坑了很多人了!)

第二行为N个用空个隔开的正整数,表示N根小木棍的长度。

 

输出格式:

 

输出文件仅一行,表示要求的原始木棍的最小可能长度

 

输入输出样例

输入样例#1: 复制
9
5 2 1 5 2 1 5 2 1
输出样例#1: 复制
6

说明

2017/08/05

数据时限修改:

-#17 #20 #22 #27 四组数据时限500ms

-#21 #24 #28 #29 #30五组数据时限1000ms

其他时限改为200ms(请放心食用)

 

搜索题,剪枝很重要

#include <bits/stdc++.h>
using namespace std;
#define maxn 100000
typedef long long ll;
#define inf 2147483647
#define ri register int

int n;
int a[maxn];
int cnt = 0//真正的总个数
int tot = 0//真正的总和
bool used[maxn];
int x;

bool cmp(int x, int y) { return x > y; }

//G组数,sum当前已装载大小,target每组目标大小,cur当前遍历到的位置
void dfs(int G, int sum, int target, int cur) {
  if (G == 0) {
    cout << target;
    exit(0);
  }
  if (sum == target) {
    dfs(G - 1, 0, target, 1);
    return;
  }
  if (sum + a[cnt] > target) //最小的都放不进直接pass
    return;
  for (int i = cur; i <= cnt; i++) {
    if (used[i])
      continue;
//平淡的回溯
    used[i] = true;
    sum += a[i];
    dfs(G, sum, target, i);
    sum -= a[i];
    used[i] = false;

    if (sum == 0 || sum + a[i] == target) //如果像这样已经满足目标的都exit不了,那么就没必要继续下去,因为接下来装的数目的也是跟之前一样的
      break;
    while (a[i] == a[i + 1]) //这个数都过不了,相同的数也别想过
      i++;
  }
}

int main() {
  ios::sync_with_stdio(false);
  // freopen("test.txt", "r", stdin);
  //  freopen("outout.txt","w",stdout);
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> x;
    if (x <= 50) {
      a[++cnt] = x;
      tot += x;
    }
  }
  sort(a + 1, a + 1 + cnt, cmp); //从大到小排次序,能优化不少
  for (int i = a[1]; i <= tot / 2; i++) {
    if (tot % i == 0)
      dfs(tot / i, 0, i, 1);
  }
  cout << tot;
  return 0;
}

 

posted @ 2018-03-08 16:03  planche  阅读(197)  评论(0编辑  收藏  举报