P1120 小木棍 [数据加强版]

小木棍[数据加强版]

题意:对于若干个小木棒,将它们拼接成若干个长木棒,使得所有长木棒的长度相等,输出长木棒最短的可能长度

思路:(深搜+剪枝)

1.考虑到长木棍的可能长度在最长小木棍长度与所有小木棍总长度之间,先打一个暴力搜索,在这个范围内dfs找答案,显然会TLE,估计有21分左右。
2.长木棍的数目为整数,所以长木棍的长度为总长度的约数。
3.如果当前处理的木棍长度加上最短的小木棒长度都超过了搜索的答案,显然不存在,剪枝。
4.如果剩余总长度加上当前处理的木棍长度都达不到目标长度,剪枝。
5.更短的木棒比长木棒更加灵活,所以对小木棒长度进行排序后从大到小枚举。
6.因为木棒在排序后有序,所以每次处理的木棒一定在上一根处理的木棒之后,传递上一根木棒的位置。
*7.(重要剪枝)某次操作中如果已经完成了一根完整的长木棍,而在更深层次的dfs中又得出答案错误,则当前循环可以直接跳出,因为对后续更短的木棒进行操作会有重复计算,剪枝。

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
//Mystery_Sky
//
#define M 1000100
#define INF 0x3f3f3f3f
#define ll long long
inline int read()
{
    int x=0, f=1;
    char c = getchar();
    while(c < '0' || c > '9') {if(c == '-') f=-1; c=getchar();}
    while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c=getchar();}
    return x * f;
}
int n, tot, l, r;
int a[M];//每根小木棍的长度。 
bool flag = false, vis[M];
int minn = INF;

inline bool check_1(int sum, int goal)
{
    if(goal - sum < minn) return true;
    else return false;
}

void dfs(int depth, int sum, int num, int goal, int rest, int pre)//已取了多少小木棒,当前在拼的小木棒总长,已经拼好了多少该长度的小木棒,目标长度,总共还剩多长,上一段在哪。 
{
    if(flag) return;
    if(depth >= tot) {
        if(sum == 0) flag = true;
        return; 
    }
    if(check_1(sum, goal)) return;//剪枝1 -> 21分
    if(rest + sum < goal) return;//剪枝5 -> 39分 
    for(int i = 1; i <= tot; i++) {//剪枝6 -> 39分 
        if(!vis[i]) {
            if(sum + a[i] > goal) return;
            break;
        }
    }
    for(int i = pre; i >= 1; i--) {
        if(flag) return;
        if(!vis[i]) {
            //if(a[i] == a[i+1] && vis[i+1] == false) continue;//剪枝4 ->39分 
            if(sum + a[i] < goal) {
                vis[i] = 1;
                dfs(depth+1, sum+a[i], num, goal, rest - a[i], i);//剪枝7->57分 
                vis[i] = 0;
            }
            else if(sum + a[i] == goal) {
                vis[i] = 1;
                dfs(depth+1, 0, ++num, goal, rest - a[i], tot);
                vis[i] = 0;
            }
            if(sum + a[i] == goal || sum == 0) break;//*重要剪枝:57分以外的53分。 
            while(a[i] == a[i-1]) i--;
        }
    }
}

int main() {
    n = read();
    bool s = true;
    for(int i = 1; i <= n; i++) {
        int x = read();
        if(x <= 50) {
            a[++tot] = x, l = max(l, a[tot]), r = r + x;
            minn = min(a[tot], minn);
            if(a[tot] != a[tot-1]) s = false;
        } 
    }
    if(s) {
        printf("%d\n", a[1]);
        return 0;
    }
    sort(a+1, a+1+tot);
    for(int i = l; i <= r; i++) {//剪枝2 -> 33分 
        if(i != l && l + minn > i) continue;//剪枝3 -> 36分 
        if(r % i == 0) {
            dfs(0, 0, 0, i, r, tot);
            if(flag) {
                printf("%d\n", i);
                break;
            }
        }
    }
    return 0;
}

剪枝真快乐

转载于:https://www.cnblogs.com/Benjamin-cpp/p/11317468.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值