【luogu】【DFS+神剪枝】小木棍(数据加强)

题目描述

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

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

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

输入输出格式

输入格式:

输入文件共有二行。

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

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

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

输出格式:

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

代码

普通DFS不加last剪枝=44分
加last剪枝=54分
加神TM剪枝=100分
蛤蛤

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <functional>
#define maxn 520
#define INF 100000000
using namespace std;

int n,L,last=1,s[maxn],suml=0,maxl=0,minl=INF,c=0; 
bool vis[maxn];

bool dfs(int cnt,int len){//剩余cnt根木棍,拼到当前还差len的长度 
    if(cnt==0&&len==0) return 1;//木棍不剩且刚好拼到目标长度就得到答案 
    int x=1;//初始化木棍从最长的开始试拼 
    if(len==0) len=L;//如果上一次刚好拼完一整根L,归零重新开始拼 
    if(len!=L) x=last;//last保存上一次是用s[last]这根木棍来减少len的,那么本次搜索就不必选比s[last]更长的木棍 
                        //这是因为在尝试拼这一根木棍的时候,s[last]以前的都已经尝试过了 
                        //如果说已经开始拼新的一根木棍了,就要从头开始搜没用过的了 
    for(int i=x;i<=n;i++){
        if(vis[i]||s[i]>len) continue;//如果已经被占用,跳过 
        if(i>=1&&!vis[i-1]&&s[i]==s[i-1]) continue;//如果相同长度的不能用,跳过 
        vis[i]=1; last=i;
        if(dfs(cnt-1,len-s[i])) return 1;
        vis[i]=0;
        if(len==L||s[i]==len) return 0;//这步很机智啊,如果开始拼新木棍了,那么任何没有被选过的木棍都要能被选进去才行
                                        //如果有一根木棍刚好可以填满,之后却爆炸了,那肯定也没解
                                        //加上这个剪枝瞬间从50分到100分啊... 
    }
    return 0;
}

int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d",&n);
    int temp;
    for(int i=1;i<=n;i++) {
        scanf("%d",&temp);
        if(temp>50) continue;
        else {
            s[++c]=temp; suml+=s[c];
        }
    } n=c;
    sort(s+1,s+1+n,greater<int>());
    for(L=s[1];L<=suml/2;L++){
        if(suml%L!=0) continue;//如果目标长度不是sum的因数就可以跳过了
        memset(vis,0,sizeof(vis));
        if(dfs(n,L)){
            printf("%d",L);
            break;
        }
    }
    if(L>suml/2) printf("%d",suml);
    return 0;
} 
posted @ 2016-11-13 12:35  Leo.Tan  阅读(228)  评论(0编辑  收藏  举报