P1120 小木棍(dfs+剪枝策略)

题目传送门

题意: 本来有一些长度相同的小木棍,我们把它们切成了若干个长度不一定相同的小木棍,且这些小木棍长度均不大于50(输入时需要手动过滤),问你原来木棍的最小可能的长度是多少?

思路: 显然原来的长度一定是合法小木棍长度和的因子,并且这个因子要大于小木棍中的最大值。我们计算总和之后枚举因子,因为要输出最小的因子,所以我们正向枚举。每次遇到因子就搜索,搜索的时候需要注意的剪枝策略:
1、如果这次的搜索是符合条件的可以直接输出答案然后退出程序。
2、如果上次搜索的木棍长度是p,下一层搜索就从p开始,从大到小枚举小木棍。
3、如果此时已拼接的木棍长度是0 或者 已拼接的长度加上目前这根刚好等于需要的长度但是并没有退出程序,应该直接退出这层函数,因为继续向下找不会有更多的选择了。

代码:

#include<bits/stdc++.h>
#pragma GCC optimize("Ofast")
#define endl '\n'
#define null NULL
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define ll long long
//#define int long long
#define pii pair<int,int>
#define pdd pair<double,double>
#define ull unsigned long long
#define all(x) x.begin(),x.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ct cerr<<"Time elapsed:"<<1.0*clock()/CLOCKS_PER_SEC<<"s.\n";
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read(){int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
return x*f;}
using namespace std;
const int N=2e5+5;
const int inf=0x3f3f3f3f;
const int mod=998244353;
const double eps=1e-7;
const double PI=acos(-1);
int tem[110],cnt=0,maxn=0,minn=100,tot=0;
void dfs(int num,int sum,int len,int p)
{
    if(num==0)
    {
        cout<<len<<endl;
        exit(0);
    }
    if(sum==len)
    {
        dfs(num-1,0,len,maxn);
        return ;
    }
    for(int i=p;i>=minn;i--)
    {
        if(tem[i]&&sum+i<=len)
        {
            tem[i]--;
            dfs(num,sum+i,len,i);
            tem[i]++;
            if(sum==0||sum+i==len)
                return ;
        }
    }
}
signed main()
{
    int n;
    cin>>n;
    int t;
    while(n--)
    {
        cin>>t;
        if(t<=50)
        {
            cnt++;
            tem[t]++;
            tot+=t;
            maxn=max(maxn,t);
            minn=min(minn,t);
        }
    }
    for(int i=maxn;i<=tot;i++)
    {
        if(tot%i==0)
        {
            dfs(tot/i,0,i,maxn);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值