小木棍

Posted captain1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小木棍相关的知识,希望对你有一定的参考价值。

传送门

这道题很明显是爆搜,但是爆搜肯定会T,我们来一步一步说。

首先木棍的原长不能小于所有木棍中最长的一段,否则那段就没用了。之后,木棍的原长也必须是总长的一个因子,而且必须是,小于等于他的一半的。这样已经减少一些不合法情况了,但是还远远不够。

之后我们考虑搜索时的操作,可以想到,我们应该先行使用长度较大的木棍,因为较小的木棍拼起来灵活一些,更适用于“补刀”,拼成功的概率更大一些。之后还有一个非常重要的操作,就是如果当前正在拼的这个木棍的剩余长度等于原长或者和现在这根木棍长度相等,但是接着搜下去却失败了,那我们直接回溯去改变原来的拼接方案。因为如果出现这种情况无法匹配的话,那么就说明这根木棍不能自己组成一根原来长度的木棍,那么它在后面也必然是没有用处的。那么我们没必要继续进行无用搜索,直接返回。

这样基本就可以过了。不过在UVA上的带有多组数据的……我的会T掉,不过mrclr大佬的代码就可以过了……看一下代码。

我的:

技术分享图片
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rg register
#define rep(i,a,n) for(rg int i = a;i <= n;i++)
#define per(i,n,a) for(rg int i = n;i >= a;i--)
#define enter putchar(‘
‘)
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
using namespace std;
typedef long long ll;
const int M = 70;
const int N = 10000005;
const int mod = 1000000009;
 
int read()
{
   int ans = 0,op = 1;
   char ch = getchar();
   while(ch < 0 || ch > 9)
   {
      if(ch == -) op = -1;
      ch = getchar();
   }
   while(ch >=0 && ch <= 9)
   {
      ans *= 10;
      ans += ch - 0;
      ch = getchar();
   }
   return ans * op; 
}

int n,a[M],cnt,maxn,x,tot; 
bool vis[M];

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

//p stands for the current length,k stands for the sum of sticks
//goal stands for the goal length,cur stands for the number of the current stick
void dfs(int p,int k,int goal,int cur)
{
   if(k * goal == tot) printf("%d
",goal),exit(0);
   if(p == goal)
   {
      dfs(0,k+1,goal,1);
      return;
   }
   if(goal - p < a[cnt]) return;
   rep(i,cur,cnt)
   {
      if(!vis[i] && p + a[i] <= goal)
      {
     vis[i] = 1;
     dfs(p+a[i],k,goal,i+1);
     vis[i] = 0;
     if(p + a[i] == goal || p == 0) break;
     while(a[i] == a[i+1]) i++;
      }
   }
}

int main()
{
   n = read();
   rep(i,1,n)
   {
      x = read();
      if(x <= 50) a[++cnt] = x,tot += a[cnt],maxn = max(maxn,a[cnt]);
   }
   sort(a+1,a+1+cnt,cmp);
   rep(i,maxn,tot >> 1) if(tot % i == 0) dfs(0,0,i,1);
   printf("%d
",tot);
   return 0;
}
View Code

 

mrclr大佬的:

技术分享图片
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("") 
#define space putchar(‘ ‘)
#define Mem(a, x) memset(a, x, sizeof(a))
#define rg register
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 70;
inline ll read()
{
  ll ans = 0;
  char ch = getchar(), last =  ;
  while(!isdigit(ch)) {last = ch; ch = getchar();}
  while(isdigit(ch)) {ans = (ans << 1) + (ans << 3) + ch - 0; ch = getchar();}
  if(last == -) ans = -ans;
  return ans;
}
inline void write(ll x)
{
  if(x < 0) x = -x, putchar(-);
  if(x >= 10) write(x / 10);
  putchar(x % 10 + 0);
}

int n;
int num[maxn], Min = INF, Max = 0, sum = 0;
int ans = INF;

bool dfs(int res, int tot, int now, int p)
{
  if(!res) {ans = min(ans, now); return 1;}
  if(tot == now) return dfs(res - 1, 0, now, Max);
  else
    {
      for(int i = p; i >= Min; --i)
    {
      if(num[i] && i + tot <= now)
        {
          num[i]--;
          if(dfs(res, tot + i, now, i)) return 1; 
          num[i]++;
          if(!tot || tot + i == now) return 0;
        }
    }
      return 0;
    }
}

void init()
{
  Mem(num, 0); Min = ans = INF; Max = sum = 0;
}

int main()
{
  while(scanf("%d", &n) && n)
    {
      init();
      for(int i = 1; i <= n; ++i)
    {
      int x = read();
      if(x > 50) continue;
      num[x]++;
      Min = min(Min, x);
      Max = max(Max, x);
      sum += x;
    }
      bool flg = 0;
      for(int i = Max; i <= (sum >> 1) && !flg; ++i)
    if(sum % i == 0)
      {
        if(dfs(sum / i, 0, i, Max)) flg = 1;
      }
      ans = min(ans, sum);
      write(ans), enter;
    }
  return 0;
}
View Code

 

以上是关于小木棍的主要内容,如果未能解决你的问题,请参考以下文章

小木棍 [数据加强版]

小木棍(爆搜减枝)

小木棍 [数据加强版]

洛谷P1120 小木棍 [数据加强版]

1442:例题3小木棍

洛谷 P1120 小木棍